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

数据库Blob转字节文件打包Zip下载无效问题求助

问题

将文件存储在数据库Blob中,无本地路径,读取为字节数据后,使用如下代码生成Zip文件供下载时,文件大小不为0KB,但生成的Zip文件无效,打开时提示“压缩文件已损坏或者格式未知”。

原代码如下:

@RequestMapping(value = "/downloadMultipleFilesAsZip", method = RequestMethod.POST)
public void downloadMultiAsZip(@RequestParam("selectedFile[]") int[] selectedFiles,
        HttpServletRequest request, HttpServletResponse response) throws FileNotFoundException, IOException {

        List<FileUpload> fileToDownload = new ArrayList<FileUpload>();
        System.out.println("==================================================");
        for (int i = 0; i<selectedFiles.length; i++) {
            System.out.println("Value of multipleFile["+i+"] is " + selectedFiles[i]);
            FileUpload fu = new FileUpload();
            fu.setFile_id(selectedFiles[i]);
            fileToDownload.add(fu);
            System.out.println("fileToDownload file_id is " + fileToDownload.get(i).getFile_id());
        }
        // store details of downloads into fileToDownload
        fileToDownload = fuMapper.downloadMultipleAsZip(fileToDownload);
        
        //Do download
        OutputStream os = null;
        
        try {                  
            response.setContentType("application/zip");
            response.setHeader("Content-Disposition", "attachment; filename=download.zip");
            
            os = response.getOutputStream();
            
            ZipOutputStream zos = new ZipOutputStream(os);
            System.out.println("================== Starting zip ==================");
            for(FileUpload fu : fileToDownload) {
                System.out.println("Zipping " + fu.getFile_name() + ".....");
                ZipEntry zipEntry = new ZipEntry(fu.getFile_name());
                
                os.write(fu.getFile_data());
                zos.finish();
                zos.putNextEntry(zipEntry);
                zos.closeEntry();
                
                } 
            System.out.println("================== Finish zipping. ==================");
            
            os.flush();
            zos.close();
            os.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
                
}
问题排查与修复

你的代码存在几个关键错误,直接导致Zip文件损坏:

  • 写入字节的对象错误:直接往原始输出流os写文件字节,而非ZipOutputStream zos,字节未经过Zip格式封装就输出,破坏了Zip结构。
  • zos.finish()调用时机错误:该方法是所有Zip条目写完后才调用的收尾方法,你在循环里每次处理文件就调用,会提前终止Zip写入流程,后续条目无法正确添加。
  • ZipEntry操作顺序颠倒:正确流程是先创建Zip条目,再写入文件字节,最后关闭条目,你的代码顺序完全错乱。

修复后的代码如下:

@RequestMapping(value = "/downloadMultipleFilesAsZip", method = RequestMethod.POST)
public void downloadMultiAsZip(@RequestParam("selectedFile[]") int[] selectedFiles,
        HttpServletRequest request, HttpServletResponse response) throws IOException {

    List<FileUpload> fileToDownload = new ArrayList<>();
    for (int fileId : selectedFiles) {
        FileUpload fu = new FileUpload();
        fu.setFile_id(fileId);
        fileToDownload.add(fu);
    }
    // 从数据库获取文件数据
    fileToDownload = fuMapper.downloadMultipleAsZip(fileToDownload);
    
    response.setContentType("application/zip");
    response.setHeader("Content-Disposition", "attachment; filename=download.zip");
    
    // 使用try-with-resources自动管理流,避免资源泄漏
    try (OutputStream os = response.getOutputStream();
         ZipOutputStream zos = new ZipOutputStream(os)) {
        
        System.out.println("================== Starting zip ==================");
        for(FileUpload fu : fileToDownload) {
            System.out.println("Zipping " + fu.getFile_name() + ".....");
            ZipEntry zipEntry = new ZipEntry(fu.getFile_name());
            
            zos.putNextEntry(zipEntry); // 先创建当前文件的Zip条目
            zos.write(fu.getFile_data()); // 往Zip流写入文件字节
            zos.closeEntry(); // 关闭当前条目
        }
        System.out.println("================== Finish zipping. ==================");
        
        zos.finish(); // 所有条目处理完成后,结束Zip写入流程
        os.flush();
    } catch (Exception e) {
        e.printStackTrace();
        // 错误时返回500状态码,方便前端感知异常
        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
    }
}

额外优化说明:

  • 用try-with-resources语法自动关闭流,避免手动关闭可能引发的异常或资源泄漏。
  • 简化了selectedFiles的遍历代码,逻辑更清晰。
  • 添加了异常时的响应状态码设置,完善错误处理逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 19:40:30