前言
最近遇到一个奇怪的需求,前端通过post请求下载压缩文件,同时会传给后端一些数据,用于生成压缩包。此时后端接口就不仅仅是生成压缩文件流输出给前端。而必须要有报错能力与异常处理能力。即如果后端报错,前端应该是下载不了文件流。
比较一般的解法
一般而言,Spring Boot生成文件流供前端下载,会直接将文件流写入到 HttpServletResponse.getOutputStream()
,然而这样会有一个问题,无论后端如何报错,前端都能成功下载文件,因为 status=200
。即如下写法:
@PostMapping(value = "/project/code/download")
public void downloadCode(@RequestBody TProjectInfo pf, HttpServletResponse response) {
// 1.生成源码文件
// 2.压缩文件
// 3.设置回复的一些参数
// 4.将压缩文件写入网络流
log.info("request param: {}", pf);
OutputStream os = null;
try {
// 配置文件下载
// 下载文件能正常显示中文
String filename = pf.getProjectName() + System.currentTimeMillis() + ".zip";
response.setHeader("Content-Disposition", "attachment;filename=" + filename);
response.setHeader("Content-Type", "application/octet-stream");
response.setContentType("application/octet-stream; charset=UTF-8");
os = response.getOutputStream();
projectService.generateCode(pf, os);
log.info("Download successfully!");
} catch (Exception e) {
log.error("Download failed: {}", e.getMessage());
throw new OptErrorException(OptStatus.FAIL.code, "文件下载失败");
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
分析原因
因为后端直接向OutputStream写入,会覆盖所有异常捕获,因此前端直接向data下取字节流数据即可。
正确解法
其实就是要后端能在错误时返回json数据,正确下载时直接取data下取字节流即可,所以使用 ResponseEntity
返回即可。
@PostMapping(value = "/project/code/download")
public ResponseEntity<InputStreamResource> downloadCode(@RequestBody TProjectInfo pf) {
log.info("request param: {}", pf);
String filename = pf.getProjectName() + System.currentTimeMillis() + ".zip";
byte[] bytes = projectService.generateCode(pf);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", String.format("attachment; filename=\"%s\"", filename));
return ResponseEntity.ok()
.headers(headers)
.contentType(MediaType.parseMediaType("application/octet-stream"))
.body(new InputStreamResource(bais));
}
这里的异常,使用RestExceptionAdvice统一处理:文章来源:https://www.toymoban.com/news/detail-514439.html
@RestControllerAdvice
public class ExceptionAdvice {
private static final Logger logger = LoggerFactory.getLogger(ExceptionAdvice.class);
/**
* 处理数据绑定异常
*
* @param bindException 数据绑定异常
* @return 统一响应基本结果
*/
@ExceptionHandler(value = BindException.class)
public ResponseEntity<Result<Object>> handleValidateException(BindException bindException) {
logger.error("数据绑定异常,{}", bindException.getMessage(), bindException);
return of(HttpStatus.BAD_REQUEST, "数据绑定异常");
}
/**
* 统一处理 405 异常
*
* @param exception 请求方法不支持异常
* @return 统一响应结果
*/
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
public ResponseEntity<Result<Object>> handleMethodNotSupportedException(HttpRequestMethodNotSupportedException exception) {
if (exception != null) {
logger.error("Http 405, {}", exception.getMessage(), exception);
}
return of(HttpStatus.METHOD_NOT_ALLOWED, "方法不被支持");
}
private ResponseEntity<Result<Object>> of(HttpStatus status, String msg) {
return ResponseEntity.status(status).body(Result.builder().code(OptStatus.FAIL.code).msg(msg).build());
}
/**
* 统一处理自定义操作错误异常
*
* @param exception 操作错误异常
* @return 统一响应结果
*/
@ExceptionHandler(value = OptErrorException.class)
public ResponseEntity<Result<Object>> handleOptErrorException(OptErrorException exception) {
return of(HttpStatus.INTERNAL_SERVER_ERROR, exception.getOptMsg());
}
/**
* 统一处理 服务器内部 异常
*
* @param e 异常
* @return 统一响应结果
*/
@ExceptionHandler(value = Throwable.class)
public ResponseEntity<Result<Object>> handle500(Throwable e) {
if (e != null) {
logger.error("Http 500, {}", e.getMessage(), e);
}
return of(HttpStatus.INTERNAL_SERVER_ERROR, "服务器内部未知错误");
}
}
以上就是解决的全过程,可能写得有点片面,纯属一点个人实践中的见解。欢迎各位交流学习!文章来源地址https://www.toymoban.com/news/detail-514439.html
到了这里,关于SpringBoot下载文件的正确方式~的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!