SpringBoot下载文件的正确解法方式
作者:habitplus007
这篇文章主要给大家介绍了关于SpringBoot下载文件的正确解法方式,SpringBoot是一款流行的框架,用于开发Web应用程序,在使用SpringBoot构建Web应用程序时,可能需要实现文件下载的功能,需要的朋友可以参考下
前言
最近遇到一个奇怪的需求,前端通过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统一处理:
@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, "服务器内部未知错误");
}
}以上就是解决的全过程,可能写得有点片面,纯属一点个人实践中的见解。
总结
到此这篇关于SpringBoot下载文件的正确解法方式的文章就介绍到这了,更多相关SpringBoot下载文件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
