前端在开发H5应用的时候发现,用FormData的方式提交单个文件在iOS的浙政钉会出现兼容性问题,报错信息大概分为两类:
Required request part 'file' is not present
the request was rejected because no multipart boundary was found
兼容性测试结果如下:
Chrome | 微信 | 浙政钉 | |
Windows | ✅ | / | / |
Android | / | ✅ | ✅ |
iOS | / | ✅ | ❌ |
小伙伴发现iOS端浙政钉的FormData数据结构大概是这样婶儿滴:
{
appendData:{},
realFormData:{}
}
然后找到了《解决系统兼容性中FormData对象包含appendData和realFormData属性》这篇博客,引入了第三方FormData,并且axios传输参数如下处理:
data:formData.realFormData || formData
但是问题依旧,最后翻遍全网,发现一篇《解决axios发送post请求上传文件到后端的问题(multipart/form-data)》文章写的比较靠谱,它是这样说的:
axios在请求发送出去之前会进行一次拦截,自动给请求设置一些参数。浙政钉会出现application/json这个参数就是因为axios设置了post请求的默认请求头,如果没有在config中指定其它请求头的话,就会使用默认的
【解决办法】:axios的config中有一个transformRequest属性,官方的解释是可以在请求发送之前人为干预。属性值是一个数组,里面可以定义一个函数,接收两个参数,分别是data和headers。data就是定义的FormData对象,headers里面则是axios预定义的请求头。有了transformRequest之后,axios就不会自动设置参数了
// `transformRequest` allows changes to the request data before it is sent to the server
// This is only applicable for request methods 'PUT', 'POST', 'PATCH' and 'DELETE'
// The last function in the array must return a string or an instance of Buffer, ArrayBuffer,
// FormData or Stream
// You may modify the headers object.
transformRequest: [function (data, headers) {
// Do whatever you want to transform the data
return data;
}],
激动人心的时刻,上示例代码!!!
前端
upload(file) {
return uploadFile({
url: '/api/files',
method: 'POST',
transformRequest: [],
headers: {
'Content-Type': 'multipart/form-data',
Authorization: getToken(),
},
data: file,
});
}
后端
@PostMapping("/files")
public String uploadFile(@RequestParam MultipartFile file) throws Exception {
return uploadSmallFileWithMd5(file);
}
/**
* 获取文件扩展名
*/
public static String getExtensionName(String filename) {
if ((filename != null) && (filename.length() > 0)) {
int dot = filename.lastIndexOf('.');
if ((dot > -1) && (dot < (filename.length() - 1))) {
return filename.substring(dot + 1);
}
}
return filename;
}
public String uploadSmallFileWithMd5(MultipartFile multipartFile) throws Exception {
String extensionName = getExtensionName(multipartFile.getOriginalFilename()).toLowerCase();
String[] fileNames =
{"doc", "docx", "xls", "xlsx", "jpg", "jpeg", "bmp", "tiff", "gif", "png", "pdf", "pptx", "dwg", "avi",
"mov", "mp4", "zip", "rar"};
if (!Arrays.asList(fileNames).contains(extensionName)) {
throw new Exception("文件格式无法识别!");
}
String filePath;
String fileName = multipartFile.getOriginalFilename();
String suffixName = fileName.substring(fileName.lastIndexOf("."));
// 兼容Linux服务器中文乱码问题
fileName = UUID.randomUUID() + suffixName;
filePath = "/Users/zhan/Desktop/" + fileName;
File file = new File(filePath);
multipartFile.transferTo(file);
return "SUCCESS";
}