Java如何从InputStream读取未压缩Zip数据并直接写入NIO ZipFileSystem
解决方案
核心思路是直接读取条目的原始压缩字节,写入目标Zip FileSystem时指定不压缩的存储模式,复用原始条目的校验和、大小参数,全程不触发解压和重新压缩流程。
前置说明
原生JDK的ZipInputStream没有公开读取原始压缩字节的接口,需要替换为Apache Commons Compress的ZipArchiveInputStream实现,该实现支持直接读取压缩状态的字节块,不会做自动解压处理。
实现步骤
- 遍历
ZipArchiveInputStream中的每个条目,判断属于需要处理的类文件还是可以直接转存的资源文件 - 资源文件条目直接调用
getRawInputStream()获取压缩状态的字节流,不要调用默认read()方法(该方法返回解压后的字节) - 在目标NIO Zip FileSystem中创建对应路径的条目,设置存储属性为
ZipEntry.STORED(不压缩),直接复用原始条目的CRC校验值、未压缩大小、压缩后大小三个参数,避免重新计算 - 将读取到的原始压缩字节直接写入目标条目输出流即可
- 类文件正常读取解压后的字节做业务处理,处理完成后写入目标FileSystem对应路径即可,压缩逻辑由FileSystem默认处理
核心代码示例
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; import java.io.InputStream; import java.net.URI; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; import java.util.Map; public class ZipPartialCopy { public void copyWithoutUncompress(InputStream networkZipStream, Path targetJarPath) throws Exception { // 初始化目标Zip的NIO FileSystem Map<String, String> env = new HashMap<>(); env.put("create", "true"); try (var targetFs = FileSystems.newFileSystem(URI.create("jar:" + targetJarPath.toUri()), env); var zis = new ZipArchiveInputStream(networkZipStream)) { ZipArchiveEntry entry; while ((entry = zis.getNextZipEntry()) != null) { Path targetPath = targetFs.getPath(entry.getName()); // 自动创建父目录 if (entry.isDirectory()) { Files.createDirectories(targetPath); continue; } // 非class资源文件直接转存原始压缩字节 if (!entry.getName().endsWith(".class")) { try (InputStream rawCompressIn = zis.getRawInputStream(); var out = Files.newOutputStream(targetPath)) { // 配置目标条目属性,指定不压缩模式 var targetEntry = new ZipArchiveEntry(entry.getName()); targetEntry.setMethod(ZipArchiveEntry.STORED); targetEntry.setCrc(entry.getCrc()); targetEntry.setSize(entry.getSize()); targetEntry.setCompressedSize(entry.getCompressedSize()); // 直接写入原始压缩字节 rawCompressIn.transferTo(out); } continue; } // 类文件走正常解压、处理、写入逻辑 try (InputStream entryIn = zis.getInputStream(entry)) { byte[] originClassBytes = entryIn.readAllBytes(); // 此处插入类文件处理逻辑 byte[] processedBytes = processClass(originClassBytes); Files.write(targetPath, processedBytes); } } } } private byte[] processClass(byte[] originBytes) { // 你的类文件处理实现 return originBytes; } }
注意事项
- 转存
STORED类型的条目时必须正确传入原始CRC、未压缩大小、压缩后大小三个参数,否则目标Zip会报校验错误无法正常读取 - 处理网络输入流时建议增加缓冲配置,避免单次IO读取字节数过小导致性能下降
内容的提问来源于stack exchange,提问作者Devan S.
相关产品推荐
相关产品推荐

