使用java.util.zip生成Zip文件时尺寸未正确写入,如何解决?
解决java.util.zip写入Zip后ZipInputStream无法获取entry尺寸的问题
这个问题我之前也碰到过,核心原因是ZipOutputStream的流式写入特性:当你直接写入文件/流内容而没有提前设置ZipEntry的尺寸时,它只会把尺寸信息写入zip文件的中央目录,而不会写到每个entry的局部文件头里。而ZipInputStream是从局部文件头读取getSize()和getCompressedSize()的值的,自然就返回-1;但ZipFile是直接读取中央目录的,所以能拿到正确值。
下面给你两种可行的解决方案:
1. 提前获取原始文件尺寸(适用于本地文件场景)
如果你的数据源是本地文件,直接通过File.length()获取原始大小,然后设置到ZipEntry中。这样ZipOutputStream会把尺寸信息写入局部文件头,ZipInputStream就能正确读取了:
try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream("output.zip"))) { File targetFile = new File("your-file.txt"); ZipEntry entry = new ZipEntry(targetFile.getName()); // 关键:提前设置原始文件大小 entry.setSize(targetFile.length()); zos.putNextEntry(entry); Files.copy(targetFile.toPath(), zos); zos.closeEntry(); }
2. 缓存动态流数据获取尺寸(适用于网络流/未知大小的流)
如果你的数据源是动态流(比如网络请求返回的流,无法提前知道大小),可以先把流数据缓存到内存或临时文件,计算出原始大小和压缩后的大小,再写入ZipOutputStream:
// 1. 先读取动态流到内存,获取原始大小 ByteArrayOutputStream rawDataBaos = new ByteArrayOutputStream(); try (InputStream inputStream = yourDynamicInputStream()) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { rawDataBaos.write(buffer, 0, bytesRead); } } byte[] rawData = rawDataBaos.toByteArray(); // 2. 预压缩数据,获取压缩后的大小 ByteArrayOutputStream compressedBaos = new ByteArrayOutputStream(); try (DeflaterOutputStream deflaterStream = new DeflaterOutputStream(compressedBaos)) { deflaterStream.write(rawData); } byte[] compressedData = compressedBaos.toByteArray(); // 3. 写入ZipOutputStream,设置两个尺寸 try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream("output.zip"))) { ZipEntry entry = new ZipEntry("dynamic-content.txt"); entry.setSize(rawData.length); entry.setCompressedSize(compressedData.length); zos.putNextEntry(entry); zos.write(compressedData); zos.closeEntry(); }
补充说明
- 如果你不需要
getCompressedSize(),只需要getSize(),那么只设置entry.setSize()就足够了,压缩后的大小会自动写入中央目录,但局部文件头还是没有,所以ZipInputStream拿不到,但至少原始大小是对的。 - 不要忘记关闭ZipEntry和流,确保资源正确释放。
内容的提问来源于stack exchange,提问作者Neph
相关产品推荐
相关产品推荐

