开发者

spring文件下载的四种方式及对比分析

目录
  • 方式一:通过ResponseEntity 方式来下载
  • 方式二:通过ResponseEntity 方式来下载
  • 方式三:通过Servlet原生下载
  • 方式四:通过ResponseEntity<byte[]> 方式来下载
  • 四种下载方式的对比
    • 1、核心特性对比
    • 2、典型场景推荐
  • 完整的代码

    方式一:通过ResponseEntity 方式来下载

    @ApiOperation(value = "下载数据文档模板")
    @GetMapping("/download-template/1")
    public ResponseEntity<Resource> downloadFile01(HttpServletRequest request) {
        try {
            Resource resource = new ClassPathResource("templates/Ubuntu-20.04.6-desktop-amd64.iso");
            // 检查资源是否存在
            if (resource.exists() || resource.isReadable()) {
                return ResponseEntity.ok()
                        .header(HttpHeaders.CONTENT_LENGTH, String.valueOf(resource.contentLength()))
                        .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + encodeFileName(request, resource.getFilename()))
                        .contentType(MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM))
                        .body(resource);
            } else {
                return ResponseEntity.notFound().build();
            }
        } catch (Exception e) {
            log.error("文件模板下载失败!", e);
            return ResponseEntity.internalServerError().build();
        }
    }
    /**
     * 根据浏览器类型编码文件名
     * @param request 请求对象
     * @param fileName 原始文件名
     * @return 编码后的文件名
     */
    private static String encodeFileName(HttpServletRequest request, String fileName) {
        String userAgent = request.getHeader("User-Agent");
        // 处理IE及Edge浏览器
        if (userAgent.contains("MSIE") || userAgent.contains("Trident") || userAgent.contains("Edge")) {
            return cn.hutool.core.net.URLEncoder.createDefault().encode(fileName, StandardCharsets.UTF_8);
        }
        // 其他浏览器使用ISO-8859-1编码
        return new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
    }

    方式二:通过ResponseEntity 方式来下载

    @ApiOperation(value = android"下载数据文档模板")
    @GetMapping("/download-template/2")
    public ResponseEntity<StreamingResponseBody> downloadFile02(HttpServletRequest request) {
        try {
            Resource resource = new ClassPathResource("templates/ubuntu-20.04.6-desktop-amd64.iso");
            StreamingResponseBody body = outputStream -> {
                FileUtil.writeToStream(resource.getFile(), outputStream);
            };
            // 检查资源是否存在
            if (resource.exists() || resource.isReadable()) {
                return ResponseEntity.ok()
                        .header(HttpHeaders.CONTENT_LENGTH, String.valueOf(resource.contentLength()))
                  			//	省略 encodeFileName 方法
                        .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + encodeFileName(request, resource.getFilename()))
                        .contentType(MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM))
                        .body(body);
            } else {
                return ResponseEntity.notFound().build();
            }
        } catch (Exception e) {
            log.error("文件模板下载失败!", e);
            return ResponseEntity.internalServerError().build();
        }
    }

    方式三:通过Servlet原生下载

    @ApiOperation(value = "下载数据文档模板")
    @GetMapping("/download-template/3")
    public void downloadFile03(HttpServletRequest request, HttpServletResponse response) throws IOException {
        Resource resource = new ClassPathResource("templates/ubuntu-20.04.6-desktop-amd64.iso");
        // 设置响应头
        response.setContentType(MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM).toString());
        response.setHeader("Content-Disposition", "attachment; filename=" + encodeFileName(request, resource.getFilename()));
      	//	这里是关键,如果设置 response.setContentLength() 则下载的文件只能是 50 多兆
        response.setContentLengthLong(resource.contentLength());
        FileUtil.writeToStream(resource.getFile(), response.getOutputStream());
    }
    

    方式四:通过ResponseEntity<byte[]> 方式来下载

    @ApiOperation(value = "下载数据文档模板")
    @GetMapping("/download-template/4")
    public ResponseEntity<byte[]> downloadFile04(HttpServletRequest request, HttpServletResponse response) throws IOException {
        try {
            Resource resource = new ClassPathResource("templates/ubuntu-20.04.6-desktop-amd64.iso");
            // 检查资源是否存在
            if (resource.exists() || resource.isReadable()) {
                return ResponseEntity.ok()
                        .header(HttpHeaders.CONTENT_LENGTH, String.valueOf(resource.contentLength()))
                        .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + encodeFileName(requestphp, resource.getFilename()))
                        .contentType(MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM))
                        .body(FileUtil.readBytes(resource.getFile()));
            } else {
                return ResponseEntity.notFound().build();
            }
        } catch (Exception e) {
            log.error("文件模板下载失败!", e);
            return ResponseEntity.internalServerError().build();
        }
    }
    

    四种下载方式的对比

    1、核心特性对比

    方式内存占用适用场景流式支持资源管理代码复杂度
    ResponseEntity<byte[]>高(全量加载)小文件(<10MB)如配置文件、图片自动释放内存低(简单封装)
    ResponseEntity<Resource>中(按需加载)动态资源(如JAR内文件、数据库Blob)✅(需手动关闭流)需显式关闭InputStream中(需处理流)
    ResponseEntity<StreamingResponseBody>极低(分块传输)超大文件(视频、日志等)✅(自动分块)自动处理流生命周期高(需分块逻辑)
    Servlet原生下载低(手动控制)需要精细控制响应头/流的场景✅(手动实现)需手动关闭流高(冗余代码)

    2、典型场景推荐

    • 快速小文件下载‌:优先使用ResponseEntity<byte[]>,直接返回字节数组,无需流控制。
    • 动态资源或加密文件‌:选择ResponseEntity<Resource>,灵活处理输入流。
    • ‌**超大文件(GB级)**‌:必须用StreamingResponseBody分块传输,避免OOM。
    • 兼容旧系统或特殊需求‌:Servlet原生下载(如需要自定义响应头逻辑)

    完整的代码

    import cn.hutool.core.io.FileUtil;
    import io.swagger.annotations.ApiOperation;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.core.io.Resource;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.MediaType;
    import org.springframework.http.MediaTypeFactory;
    import org.springframework.http.ResponseEntity;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.sandroidervlet.mvc.method.annotation.StreamingResponseBody;
    import Javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.nio.charset.StandardCharsets;
    /**
     * description
     *
     * @author lijin
     * @since 2025-08-12 14:05
     */
    @Slf4j
    @Controller
    @RequestMapping("/cycle-data/document01")
    public class DownloadController {
        @ApiOperation(value = "下载数据文档模板")
        @GetMapping("/download-template/1")
        public ResponseEntity<Resource> downloadFile01(HttpServletRequest request) {
            try {
                Resource resource = new ClassPathResource("templates/ubuntu-20.04.6-desktop-amd64.iso");
                // 检查资源是否存在
                if (resource.exists() || resource.isReadable()) {
                    return ResponseEntity.ok()
                            .header(HttpHeaders.CONTENT_LENGTH, String.valueOf(resource.contentLength()))
                            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + encodeFileName(request, resource.getFilename()))
                            .contentType(MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM))
                            .body(resource);
                } else {
                    return ResponseEntity.notFound().build();
                }
            } catch (Exception e) {
                log.error("文件模板下载失败!", e);
                return ResponseEntity.internalServerError().build();
            }
        }
        @ApiOperation(value = "下载数据文档模板")
        @GetMapping("/download-template/2")
        public ResponseEntity<StreamingResponseBody> downloadFile02(HttpServletRequest request) {
            try {
                Resource resource = new ClassPathResource("templates/ubuntu-20.04.6-desktop-amd64.iso");
                StreamingResponseBody body = outputStream -> {
                    FileUtil.writeToStream(resource.getFile(), outputStream);
                };
                // 检查资源是否存在
                if (resource.exists() || resource.isReadable()) {
                    return ResponseEntity.ok()
                            .header(HttpHeaders.CONTENT_LENGTH, String.valueOf(resource.contentLength()))
                            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + encodeFileName(request, resource.getFilename()))
                            .contentType(MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM))
                            .body(body);
                } else {
                    return ResponseEntity.notFound().build();
                }
            } catch (Exception e) {
                log.error("文件模板下载失败!", e);
                return ResponseEntity.internalServerError().build();
            }
        }
        @ApiOperation(value = "下载数据文档模板")
        @GetMapping("/download-template/3")
        public void downloadFile03(HttpServletRequest request, HttpServletResponse response) throws IOException {
            Resource resource = new ClassPathResource("templates/ubuntu-20.04.6-desktop-amd64.iso");
            // 设置响应头
            response.setContentType(MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM).toString());
            response.setHeader("Content-Disposition", "attachment; filename=" + encodeFileName(request, resource.getFilename()));
            response.setContentLengthLong(resource.contentLength());
            FileUtil.writeToStream(resource.getFile(), response.getOutputStream());
        }
        @ApiOperation(value = "下载数据文档模板")
        @GetMapping("/download-template/4")
        public ResponseEntity<byte[]> downloadFile04(HttpServletRequest request, HttpServletResponse response) throws IOException {
            try {
                Resource resource = new ClassPathResource("templates/ubuntu-20.04.6-desktop-amd64.iso");
                // 检查资源是否存在
                if (resource.exists() || resource.isReadable()) {
                    return ResponseEntity.ok()
                            .header(HttpHeaders.CONTENT_LENGTH, String.valueOf(resource.contentLength()))
                            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + encodeFileName(request, resource.getFilename()))
                            .contentType(MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM))
                            .body(FileUtil.readBytes(resource.getFile()));
                } else {
                    return ResponseEntity.notFound().build();
                }
            } catch (Excepti编程客栈on e) {
                log.error("文件模板下载失败!", e);
                return ResponseEntity.internalServerError().build();
            }
        }
        /**
         * 根据浏览器类型编码文件名
         * @param request 请求对象
         * @param fileName 原始文件名
         * @return 编码后的文件名
         */
        private static String encodeFileName(HttpServletRequest request, String fileName) {
            String userAgent = request.getHeader("User-Agent");
            // 处理IE及Edge浏览器
            if (userAgent.contains("MSIE") || userAgent.contains("Trident") || userAgent.contains("Edge")) {
                return cn.hutool.core.net.URLEncoder.createDefault().encode(fileName, StandardCharsets.UTF_8);
            }
            // 其他浏览器使用ISO-8859-1编码
            return new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
     RBwLoEbf   }
    }

    到此这篇关于spring文件下载的四种方式及对比分析的文章就介绍到这了,更多相关spring文件下载内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

    0

    上一篇:

    下一篇:没有了

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜