文件导出异常处理Failed to parse multipart servlet request; nested exception is java.io.IOException:
前言
最近做了个Excel导出功能,流程是先生成文件在本地,然后通过Nginx代理出本地路径,返回代理的访问地址,前端通过fetch 请求下载文件,导出文件报错:Failed to parse multipart servlet request; nested exception is java.io.IOException:
The temporary upload location [C:\Windows\Temp\tomcat.8567183481807092481.9901\work\Tomcat\localhost\ROOT] is not valid
Failed to parse multipart servlet request; nested exception is java.io.IOException:
The temporary upload location [C:\Windows\Temp\tomcat.8567183481807092481.9901\work\Tomcat\localhost\ROOT] is not valid
一 原请求方法
1.1 前端请求
function initUpload() {
uploadInstance = upload.render({
elem: '#uploadHomeVisitsBtn', // 上传按钮的选择器
auto: false, // 设置为自动上传
multiple: false, // 多文件上传
accept: 'images', // 只允许图片
exts: 'jpg|png|gif|bmp|jpeg|webp|svg', // 允许的文件后缀
url: '/springApi/inducts/importVisit', // 后台接口 URL (请替换成实际接口地址)
choose: function (obj) {
// 检查当前文件列表是否超过最大数量
if (fileList.length === maxFiles || fileList.length > maxFiles) {
PromptModal("警告", "最多只能上传9张图片");
return; // 阻止上传
}
// 点击上传就禁用保存按钮
$("#idSave").attr("disabled", "disabled");
// 创建 FormData 对象
var formData = new FormData();
obj.preview(function (index, file, result) {
formData.append("file", file); // 添加文件到 FormData
formData.append('urlPrefix', prefix); // 添加额外的参数
// 使用 fetch 上传文件
fetch('/springApi/inducts/importVisit', {
method: 'POST',
body: formData,
credentials: 'include' // 显式携带 cookie
}).then(resp => resp.json())
.then(res => {
if (res.code == 0) {
// 上传成功,更新文件列表
fileList.push({
fileName: res.data.name,
previewUrl: res.data.url
});
updateFileList();
} else {
// 上传失败,弹出提示并重置按钮状态
uploadInstance.config.elem.val('');
hyd.alert.showAlert(`上传失败:【 ${res.message} 】`);
}
// 最后处理恢复按钮
$("#idSave").removeAttr("disabled");
}).catch(error => {
// 上传过程中发生错误,处理异常
uploadInstance.config.elem.val('');
hyd.alert.showAlert(`上传失败:【 ${error} 】`);
// 上传完成后,恢复保存按钮状态
$("#idSave").removeAttr("disabled");
})
});
},
error: function (index, upload) {
// 上传失败的回调
hyd.alert.showAlert("上传失败,请重试或者联系管理员");
// 失败后解禁保存按钮
$("#idSave").removeAttr("disabled");
},
});
}
1.2 后端处理
public ResultVO importVisit(MultipartFile file,String urlPrefix) {
if (file.isEmpty()) {
return ResultFactory.error("现场照片不能为空");
}
//写入本地 D:\\huabin\\bizfile\\wechatfile\\
String format = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
String basePath = importVisitPath + format + File.separator;
File dir = new File(basePath);
if (!dir.exists()) {
dir.mkdirs(); // 创建目录
}
//文件名
String fileName = UUID.randomUUID() + "_" + file.getOriginalFilename();
File file1 = new File(dir, fileName);
try {
file.transferTo(file1);
} catch (IOException e) {
return ResultFactory.error("上传现场照片失败");
}
FollowupPicture followupPicture = new FollowupPicture();
//代理路径 /wechatfile/2025-02-07/e0dccef4-8a05-4a9d-bc41-dd6a397d7f84_photo2.png
String fullPath = urlPrefix + sharePath + format + BaseConstants.SLASH + fileName;
followupPicture.setUrl(fullPath);
followupPicture.setName(fileName);
return ResultFactory.success(followupPicture);
}
在上述的后端处理中,file.transferTo() 方法会把前端传递的文件对象生成到一个临时文件,然后在把临时文件转移到目标路径,这就很容易造成一个问题,那就是这个临时文件路径一般都是没有给外部权限的,就容易报错:Failed to parse multipart servlet request; nested exception is java.io.IOException:
The temporary upload location [C:\Windows\Temp\tomcat.8567183481807092481.9901\work\Tomcat\localhost\ROOT] is not valid
二 处理 办法
2.1 更改临时文件路径
1.yml配置目标路径
server:
port: 8770
tomcat:
basedir: C:/users/song/tomcatTmp
2.java中指定Tomcat的临时文件路径
@Bean
MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
String location = "C:/users/song/tomcatTmp";
File tmpFile = new File(location);
if (!tmpFile.exists()) {
tmpFile.mkdirs();
}
factory.setLocation(location);
return factory.createMultipartConfig();
}
3.配置Jvm 启动参数
-Djava.io.tmpdir=D:/TEST/temp
说实话,我用了上述方法,在本地是实现了,文件生成时是在指定的临时路径,但是在生产环境,就是不行,有大佬知道吗,劳烦提点提点?故我采取了另一种方式,采用文件流的方式处理,这种方式就不需要先生成在零时文件路径在转移至目标路径;
2.2 采用文件流的方式处理
2.21 前端调整
function initUpload() {
uploadInstance = upload.render({
elem: '#uploadHomeVisitsBtn', // 上传按钮的选择器
auto: false, // 设置为自动上传
multiple: false, // 多文件上传
accept: 'images', // 只允许图片
exts: 'jpg|png|gif|bmp|jpeg|webp|svg', // 允许的文件后缀
url: '/springApi/inducts/uploadVisit', // 后台接口 URL (请替换成实际接口地址)
choose: function (obj) {
// 检查当前文件列表是否超过最大数量
if (fileList.length === maxFiles || fileList.length > maxFiles) {
PromptModal("警告", "最多只能上传9张图片");
return; // 阻止上传
}
// 点击上传就禁用保存按钮
$("#idSave").attr("disabled", "disabled");
obj.preview(function (index, file, result) {
// 使用 fetch 上传文件
fetch('/springApi/inducts/uploadVisit', {
method: 'POST',
headers: {
'Content-Type': 'application/octet-stream',
'X-Filename': file.name
},
body: file,
credentials: 'include' // 显式携带 cookie
}).then(resp => resp.json())
.then(res => {
if (res.code == 0) {
// 上传成功,更新文件列表
fileList.push({
fileName: res.data.name,
previewUrl: res.data.url
});
updateFileList();
} else {
// 上传失败,弹出提示并重置按钮状态
uploadInstance.config.elem.val('');
hyd.alert.showAlert(`上传失败:【 ${res.message} 】`);
}
// 最后处理恢复按钮
$("#idSave").removeAttr("disabled");
}).catch(error => {
// 上传过程中发生错误,处理异常
uploadInstance.config.elem.val('');
hyd.alert.showAlert(`上传失败:【 ${error} 】`);
// 上传完成后,恢复保存按钮状态
$("#idSave").removeAttr("disabled");
})
});
},
error: function (index, upload) {
// 上传失败的回调
hyd.alert.showAlert("上传失败,请重试或者联系管理员");
// 失败后解禁保存按钮
$("#idSave").removeAttr("disabled");
},
});
}
2.2.2 后端调整
public ResultVO uploadVisit(HttpServletRequest request) {
//获取原文件名
String originalFileName = request.getHeader("X-Filename");
if (originalFileName == null) {
return ResultFactory.error("文件名缺失");
}
try {
originalFileName = URLDecoder.decode(originalFileName, String.valueOf(StandardCharsets.UTF_8));
} catch (UnsupportedEncodingException e) {
return ResultFactory.error("上传失败,文件名解密错误: " + e.getMessage());
}
String suffix = "";
int dotIndex = originalFileName.lastIndexOf('.');
if (dotIndex != -1) {
suffix = originalFileName.substring(dotIndex);
}
String uuid = UUID.randomUUID().toString();
String fileName = uuid + suffix;
//写入本地 D:\\huabin\\bizfile\\wechatfile\\
String format = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
String basePath = importVisitPath + format + BaseConstants.SLASH;
File dir = new File(basePath);
if (!dir.exists()) {
dir.mkdirs();
}
File outFile = new File(dir, fileName);
try (ServletInputStream inputStream = request.getInputStream();
OutputStream outputStream = Files.newOutputStream(outFile.toPath())) {
byte[] buffer = new byte[8192];
int len;
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
FollowupPicture followupPicture = new FollowupPicture();
//代理路径 /wechatfile/2025-02-07/e0dccef4-8a05-4a9d-bc41-dd6a397d7f84_photo2.png
String fullPath = sharePath + format + BaseConstants.SLASH + fileName;
followupPicture.setUrl(fullPath);
followupPicture.setName(fileName);
return ResultFactory.success(followupPicture);
} catch (IOException e) {
return ResultFactory.error("上传失败: " + e.getMessage());
}
}
更多推荐
所有评论(0)