Spring Boot下载Zip文件异常:文件损坏且大小不符问题排查
问题原因与修复方案
核心问题分析
- Content-Length设置错误:
ZipFile.size()返回的是压缩包内所有文件的未压缩总大小,并非实际Zip文件的磁盘字节大小。浏览器依据错误的Content-Length提前终止下载,导致文件不完整。 - HttpServletResponse注入方式不安全:直接通过
@Autowired注入HttpServletResponse会引发线程安全问题——它属于请求作用域对象,多请求并发时会出现资源冲突。 - 文件路径重复硬编码:代码中重复写入文件路径,既容易出错,也降低了代码维护性。
修复步骤
- 改用
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
相关产品推荐
相关产品推荐

