调用API时实现Zip文件下载功能的技术咨询
实现方案(默认基于Spring Boot生态)
核心逻辑
API返回zip文件的核心是正确设置HTTP响应头,再将zip文件的字节流写入响应输出流即可触发客户端自动下载,关键响应头配置如下:
Content-Type固定为application/zip,告知客户端返回的是zip格式文件Content-Disposition配置为attachment;filename="xxx.zip",标识该资源为可下载附件,同时指定下载后的默认文件名- 可选配置
Content-Length,可让客户端显示准确的下载进度
场景1:已经生成了本地zip文件,直接返回该文件
对应Controller层代码示例:
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @RestController @RequestMapping("/api") public class ZipDownloadController { // 替换为你实际生成的zip文件存储路径 private static final String ZIP_FILE_PATH = "/xxx/xxx/generated.zip"; @GetMapping("/download/zip") public void downloadLocalZip(HttpServletResponse response) { File zipFile = new File(ZIP_FILE_PATH); // 编码文件名,避免中文乱码 String encodeFileName = URLEncoder.encode(zipFile.getName(), StandardCharsets.UTF_8) .replaceAll("\\+", "%20"); // 设置响应头 response.setContentType("application/zip"); response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + encodeFileName); response.setContentLengthLong(zipFile.length()); // 读取本地文件写入响应流 try ( FileInputStream fis = new FileInputStream(zipFile); OutputStream os = response.getOutputStream(); ) { byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) != -1) { os.write(buffer, 0, len); } os.flush(); } catch (IOException e) { // 按需添加异常处理逻辑 e.printStackTrace(); } } }
场景2:优化方案,zip不落地磁盘直接生成返回
不需要将生成的zip写入本地磁盘,直接把压缩流写入响应,节省磁盘IO开销,代码示例:
@GetMapping("/download/zip") public void downloadZipOnFly(HttpServletResponse response) { // 替换为你要压缩的文件列表 List<File> files = Arrays.asList(new File("/xxx/file1.txt"), new File("/xxx/file2.jpg")); String zipFileName = "导出文件.zip"; // 编码文件名,避免中文乱码 String encodeFileName = URLEncoder.encode(zipFileName, StandardCharsets.UTF_8) .replaceAll("\\+", "%20"); // 设置响应头 response.setContentType("application/zip"); response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + encodeFileName); // 直接将压缩流写入响应 try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) { for (File file : files) { try (FileInputStream fis = new FileInputStream(file)) { ZipEntry zipEntry = new ZipEntry(file.getName()); zipOut.putNextEntry(zipEntry); byte[] bytes = new byte[1024]; int length; while((length = fis.read(bytes)) >= 0) { zipOut.write(bytes, 0, length); } zipOut.closeEntry(); } } } catch (IOException e) { // 按需添加异常处理逻辑 e.printStackTrace(); } }
前端调用注意事项
如果用Ajax/Axios等方式调用接口,需要指定响应类型为blob,再手动触发下载,示例如下:
axios.get('/api/download/zip', { responseType: 'blob' // 必须指定响应类型为二进制blob }).then(res => { // 从响应头解析文件名,也可以自定义 const fileName = decodeURIComponent(res.headers['content-disposition'].split('filename*=utf-8\'\'')[1]) const blob = new Blob([res.data], { type: 'application/zip' }) const downloadUrl = window.URL.createObjectURL(blob) const aTag = document.createElement('a') aTag.href = downloadUrl aTag.download = fileName aTag.click() window.URL.revokeObjectURL(downloadUrl) })
注意点
- 流操作使用try-with-resources语法,自动关闭资源避免泄漏
- 大文件压缩下载建议添加分块处理逻辑,避免内存溢出
- 文件名编码使用
filename*=utf-8''格式,兼容绝大多数现代浏览器,避免中文乱码
内容的提问来源于stack exchange,提问作者gin
相关产品推荐
相关产品推荐

