如何直接将数据库中deflated数据打包为Zip/GZip/7z文件?
问题:直接将已deflate压缩的数据打包为Zip文件(避免先解压再压缩)
数据库中存储的是deflated压缩格式的数据,目前应用支持下载解压后的.txt文件,现在要实现直接将该数据打包成.zip文件供下载,不想用先解压再重新压缩的方式(如下代码示例):
// byte[] deflated = ...; // File zipFile = ...; try (FileOutputStream fos = new FileOutputStream(zipFile); ZipOutputStream zos = new ZipOutputStream(fos)) { ZipEntry entry = new ZipEntry("data.txt"); zos.putNextEntry(entry); zos.write(inflate(deflated)); } catch (Exception e) { // handling }
尝试过entry.setMethod(ZipEntry.STORED)但没成功,搜不到针对已压缩数据的解决方案,也可以考虑GZip或7z格式。
可行方案
方案1:复用DEFLATE压缩数据生成Zip文件
Zip格式本身基于DEFLATE算法,所以可以直接复用数据库里的压缩数据,关键是要手动补充ZipEntry必需的元数据——之前用STORED失败是因为该方法用于存储未压缩数据,我们需要用DEFLATED方法,并设置三个核心参数:
// 从数据库获取的deflate压缩数据 byte[] deflatedData = ...; // 需提前和压缩数据一起存储的元数据:原始未压缩数据的大小和CRC32校验值 long originalUncompressedSize = ...; long originalCrc32 = ...; try (FileOutputStream fos = new FileOutputStream("output.zip"); ZipOutputStream zos = new ZipOutputStream(fos)) { ZipEntry entry = new ZipEntry("data.txt"); // 设置压缩方法为DEFLATED,和已有数据的压缩格式匹配 entry.setMethod(ZipEntry.DEFLATED); // 必须设置的三个元数据 entry.setSize(originalUncompressedSize); // 原始未压缩数据的字节数 entry.setCompressedSize(deflatedData.length); // 已压缩数据的字节数 entry.setCrc(originalCrc32); // 原始数据的CRC32校验值 // 写入条目并直接复用压缩数据 zos.putNextEntry(entry); zos.write(deflatedData); zos.closeEntry(); } catch (Exception e) { // 异常处理逻辑 e.printStackTrace(); }
注意:原始数据的大小和CRC32值必须提前存储在数据库中,否则无法生成合法的Zip文件——Zip格式依赖这些信息验证文件完整性和恢复原始数据。
方案2:直接生成GZip文件(更轻量)
如果可以接受GZip格式,实现会更简单,因为GZip本质就是DEFLATE算法加了固定的头部和尾部。只需给数据库里的deflate数据添加GZip头和尾即可:
byte[] deflatedData = ...; long originalCrc32 = ...; long originalUncompressedSize = ...; try (FileOutputStream fos = new FileOutputStream("output.gz")) { // 写入GZip固定头部(魔数+压缩方法+标志位等) fos.write(new byte[]{0x1F, (byte)0x8B, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}); // 直接写入已有的deflate压缩数据 fos.write(deflatedData); // 写入CRC32校验值(小端字节序) fos.write(intToLittleEndian((int)originalCrc32)); // 写入原始未压缩数据的大小(小端字节序,取低32位) fos.write(intToLittleEndian((int)(originalUncompressedSize & 0xFFFFFFFFL))); } catch (Exception e) { e.printStackTrace(); } // 辅助方法:将int转为小端字节序的字节数组 private static byte[] intToLittleEndian(int value) { return new byte[]{ (byte)(value & 0xFF), (byte)((value >> 8) & 0xFF), (byte)((value >> 16) & 0xFF), (byte)((value >> 24) & 0xFF) }; }
这个方案无需额外依赖,生成的GZip文件可以直接被标准解压工具识别,用户下载后解压就能得到原始txt文件。
方案3:7z格式(需第三方库)
如果必须用7z格式,可以使用Apache Commons Compress库,它支持直接添加已压缩数据到7z包,但需要确保压缩算法匹配。不过该方案需要引入依赖,实现复杂度高于前两个,因此优先推荐前两种方案。
内容的提问来源于stack exchange,提问作者Stefan Warminski
相关产品推荐
相关产品推荐

