0


SpringBoot下载文件的正确方式~

Spring Boot 配合 axios 实现文件下载功能

前言

最近遇到一个奇怪的需求,前端通过post请求下载压缩文件,同时会传给后端一些数据,用于生成压缩包。此时后端接口就不仅仅是生成压缩文件流输出给前端。而必须要有报错能力与异常处理能力。即如果后端报错,前端应该是下载不了文件流。

比较一般的解法

一般而言,Spring Boot生成文件流供前端下载,会直接将文件流写入到

HttpServletResponse.getOutputStream()

,然而这样会有一个问题,无论后端如何报错,前端都能成功下载文件,因为

status=200

。即如下写法:

@PostMapping(value ="/project/code/download")publicvoiddownloadCode(@RequestBodyTProjectInfo 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());thrownewOptErrorException(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")publicResponseEntity<InputStreamResource>downloadCode(@RequestBodyTProjectInfo pf){
   log.info("request param: {}", pf);String filename = pf.getProjectName()+System.currentTimeMillis()+".zip";byte[] bytes = projectService.generateCode(pf);ByteArrayInputStream bais =newByteArrayInputStream(bytes);HttpHeaders headers =newHttpHeaders();
   headers.add("Content-Disposition",String.format("attachment; filename=\"%s\"", filename));returnResponseEntity.ok().headers(headers).contentType(MediaType.parseMediaType("application/octet-stream")).body(newInputStreamResource(bais));}

这里的异常,使用RestExceptionAdvice统一处理:

@RestControllerAdvicepublicclassExceptionAdvice{privatestaticfinalLogger logger =LoggerFactory.getLogger(ExceptionAdvice.class);/**
     * 处理数据绑定异常
     *
     * @param bindException 数据绑定异常
     * @return 统一响应基本结果
     */@ExceptionHandler(value =BindException.class)publicResponseEntity<Result<Object>>handleValidateException(BindException bindException){
        logger.error("数据绑定异常,{}", bindException.getMessage(), bindException);returnof(HttpStatus.BAD_REQUEST,"数据绑定异常");}/**
     * 统一处理 405 异常
     *
     * @param exception 请求方法不支持异常
     * @return 统一响应结果
     */@ExceptionHandler(value =HttpRequestMethodNotSupportedException.class)publicResponseEntity<Result<Object>>handleMethodNotSupportedException(HttpRequestMethodNotSupportedException exception){if(exception !=null){
            logger.error("Http 405, {}", exception.getMessage(), exception);}returnof(HttpStatus.METHOD_NOT_ALLOWED,"方法不被支持");}privateResponseEntity<Result<Object>>of(HttpStatus status,String msg){returnResponseEntity.status(status).body(Result.builder().code(OptStatus.FAIL.code).msg(msg).build());}/**
     * 统一处理自定义操作错误异常
     *
     * @param exception 操作错误异常
     * @return 统一响应结果
     */@ExceptionHandler(value =OptErrorException.class)publicResponseEntity<Result<Object>>handleOptErrorException(OptErrorException exception){returnof(HttpStatus.INTERNAL_SERVER_ERROR, exception.getOptMsg());}/**
     * 统一处理 服务器内部 异常
     *
     * @param e 异常
     * @return 统一响应结果
     */@ExceptionHandler(value =Throwable.class)publicResponseEntity<Result<Object>>handle500(Throwable e){if(e !=null){
            logger.error("Http 500, {}", e.getMessage(), e);}returnof(HttpStatus.INTERNAL_SERVER_ERROR,"服务器内部未知错误");}}

以上就是解决的全过程,可能写得有点片面,纯属一点个人实践中的见解。欢迎各位交流学习!

标签: spring boot java spring

本文转载自: https://blog.csdn.net/dubulingbo/article/details/129641709
版权归原作者 habitplus007 所有, 如有侵权,请联系我们删除。

“SpringBoot下载文件的正确方式~”的评论:

还没有评论