业务背景:几乎很多app都有文件交互的功能,诸如头像管理、材料上传等等。在我们的一次在线签约场景中,需要在websoucket场景下来实现文件上传功能。也算是前一篇文章《【JAVA、Tomcat、WebSocket】配置文件保存路径、实现前端直接访问本地文件路径,后端实时保存代码实现》所遇到问题的解决方案。
一、问题描述及解决
具体文件上传业务代码在如上标注的文章中,可见前端传入到接口的格式是String类型的Base64字符串,这里面有第一个坑就是以String接的话,会存在”/r/n“这些转义符,我们需要做一个replace即byte[] bytes = Base64.getDecoder().decode(base64.replace("\r\n",""));
接下来在我们自己本地测试,使用整套流程都没有问题。然而代码放到了服务器上就无法将文件写入到服务器指定位置。
这里面写入先后用到的是FileOutputStream、BufferedOutputStream,再找问题时找到一篇大概很有帮助的文章,同时也参考了很多方案都没有起效果。
Java踩坑笔记 —— 关于OutPutStream流无法在Process应用场景写入失败解决方案
于是,转变思路。我们平常封装好的通过File或者MultipartFile格式的工具类来接收前端传入的文件,一般工具类如下:
public class FileUtil {
public static String uploadFile(MultipartFile file) {
//获取文件上传路径
String path = "/tomcat/webapps/file";
// String path = "D:\\file";
String originalFilename = file.getOriginalFilename();
//获取文件后缀
String substring = originalFilename.substring(originalFilename.lastIndexOf("."));
File targetFile ;
//拼接新文件名(时间戳+5位数随机数)
String photoFileName=System.currentTimeMillis() + RandomUtil.getAllNum(5) + substring;
try {
File upload = new File(path);
if(!upload.exists()) {
upload.mkdirs();
}
targetFile = new File(path, photoFileName);
//将上传的文件写到服务器上指定的目录。
file.transferTo(targetFile);
} catch (Exception e) {
e.printStackTrace();
}
return "/file/" + photoFileName;
}
}
但是缺陷就是需要前端转换传输格式,虽然稳妥,但在原基础上工作量增加很多。又经过一阵子寻找,最终找到一个更好的方案,在原基础上同样吧String类型的Base64字符串转换成byte 用ByteArrayInputStream、FileOutputStream写入完成,并且返还图片地址、图片名称。
public class ToImgUtil {
private String base;
public ToImgUtil() {
}
public ToImgUtil(String base) {
this.base = base;
}
public String getBase() {
return base;
}
public void setBase(String base) {
this.base = base;
}
/**
* 为文件重新命名,命名规则为当前系统时间毫秒数
*
* @return string
*/
private static String getFileNameNew() {
SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMddHHmmssSSS");
return fmt.format(new Date());
}
public static Map ToImg(String base){
//获取年月日
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
//将年、月、日存入数组
String[] time = sdf.format(new Date()).split("-");
// 文件保存地址 默认地址为xxxx/年/月/日
// String destDir = "xxxx/"+time[0]+"/"+time[1]+"/"+time[2];;
String destDir = "/tomcat/webapps/file";
/* response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST");*/
base=base.replaceAll("data:image/png;base64,","");
BASE64Decoder decoder = new BASE64Decoder();
byte[] imageByte = null;
try{
imageByte = decoder.decodeBuffer(base);
for (int i = 0; i < imageByte.length; ++i) {
if (imageByte[i] < 0) {// 调整异常数据
imageByte[i] += 256;
}
}
} catch (Exception e) {
e.printStackTrace();
}
if (imageByte.length>0) {
try {
String filepath = destDir;
File destfile = new File( filepath);
if (!destfile.exists()) {
destfile.mkdirs();
}
//文件新名称
String fileNameNew = getFileNameNew() + ".png";
File f = new File(destfile.getAbsoluteFile() + File.separator + fileNameNew);
// 将字符串转换成二进制,用于显示图片
// 将上面生成的图片格式字符串 imgStr,还原成图片显示
InputStream in = new ByteArrayInputStream(imageByte);
FileOutputStream fos = new FileOutputStream(f);
// BufferedOutputStream bos = new BufferedOutputStream(fos);
byte[] buf = new byte[1024];
int length;
length = in.read(buf, 0, buf.length);
while (length != -1) {
fos.write(buf,0,length);
length = in.read(buf);
}
fos.flush();
fos.close();
in.close();
Map<String,String> map = new HashedMap<>();
map.put("destDir",destDir);
map.put("fileNameNew",fileNameNew);
return map;
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
return null;
}
}
二、资料引申
Socket通讯使用遇到(BufferedReader||Write)(Output||InputStream)(Object Input||Output Stream)的问题/阻塞/空