You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot下载Zip文件异常:文件损坏且大小不符问题排查

问题原因与修复方案

核心问题分析

  1. Content-Length设置错误:ZipFile.size()返回的是压缩包内所有文件的未压缩总大小,并非实际Zip文件的磁盘字节大小。浏览器依据错误的Content-Length提前终止下载,导致文件不完整。
  2. HttpServletResponse注入方式不安全:直接通过@Autowired注入HttpServletResponse会引发线程安全问题——它属于请求作用域对象,多请求并发时会出现资源冲突。
  3. 文件路径重复硬编码:代码中重复写入文件路径,既容易出错,也降低了代码维护性。

修复步骤

  • 改用File.length()获取实际Zip文件的字节大小,设置正确的Content-Length响应头。
  • 将HttpServletResponse作为方法参数注入,符合Spring请求作用域的规范。
  • 用File对象统一管理文件路径,避免重复硬编码。

修复后的完整代码

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;

@RestController
public class DownloadController {
    private static final Logger log = LoggerFactory.getLogger(DownloadController.class);

    @GetMapping("/download-batch-photos")
    public void downloadPhotoBatchTest(HttpServletResponse httpResponse) throws IOException {
        File zipFile = new File("/Users/john/59c49360-3b55-4f97-910e-9f33907f10cb.zip");
        
        // 设置响应头
        httpResponse.setContentType("application/zip");
        httpResponse.setHeader("Content-Disposition", "attachment; filename=\"" + zipFile.getName() + "\"");
        httpResponse.setHeader("Content-Length", String.valueOf(zipFile.length()));
        
        try (OutputStream out = httpResponse.getOutputStream();
             FileInputStream in = new FileInputStream(zipFile)) {
            byte[] buffer = new byte[4096];
            int length;
            while ((length = in.read(buffer)) > 0) {
                out.write(buffer, 0, length);
            }
            out.flush();
        } catch (IOException e) {
            log.error("写入压缩包到响应流失败", e);
            // 可选:返回错误状态码
            httpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }
    }
}

额外优化建议

  • 避免硬编码文件路径,可通过配置文件读取(如@Value("${zip.download.path}"))。
  • 增加文件存在性校验,防止文件不存在时抛出异常:
    if (!zipFile.exists() || !zipFile.isFile()) {
        httpResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
        return;
    }
    
  • 处理大文件时,可使用Spring的Resource和ResourceHttpMessageConverter简化代码,自动处理流和响应头:
    @GetMapping("/download-batch-photos")
    public ResponseEntity<Resource> downloadPhotoBatchTest() throws IOException {
        File zipFile = new File("/Users/john/59c49360-3b55-4f97-910e-9f33907f10cb.zip");
        Resource resource = new FileSystemResource(zipFile);
        
        return ResponseEntity.ok()
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + zipFile.getName() + "\"")
                .contentLength(zipFile.length())
                .body(resource);
    }
    

内容的提问来源于stack exchange,提问作者Dolphin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 03:42:16