使用Zip4J处理带密码zip压缩包提示文件不存在异常如何解决
问题原因
- 你当前调用的
ZipFile.addFiles()API是面向磁盘文件设计的,会主动校验传入的File对象对应的本地磁盘文件是否存在,你本地不存在/test/ZipThis.txt路径的文件所以直接抛出异常 - 其次你的需求是全程不操作文件系统,完全基于字节数组输入输出,使用
addFiles()、传入磁盘路径构造ZipFile的写法本身就不符合需求,应该用zip4j提供的流操作API实现全内存压缩。
修正后的实现方案
核心调整点
- 压缩环节使用
addStream()方法替代addFiles(),直接传入字节数组对应的ByteArrayInputStream,不需要关联磁盘文件 - 构造压缩输出时用
ByteArrayOutputStream承接生成的压缩包字节,不写入磁盘 - 全程仅使用内存流,无任何文件系统IO操作
完整代码
import net.lingala.zip4j.io.inputstream.ZipInputStream; import net.lingala.zip4j.io.outputstream.ZipOutputStream; import net.lingala.zip4j.model.LocalFileHeader; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.model.enums.EncryptionMethod; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; public class ZipByteArrayUtils { /** * 压缩字节数组为带密码的zip包字节数组 * @param content 待压缩的文件内容字节数组 * @param fileNameInZip 压缩包内的文件名 * @param password 压缩密码 * @return 压缩后的zip包字节数组 * @throws IOException */ public static byte[] compressWithPassword(byte[] content, String fileNameInZip, char[] password) throws IOException { ZipParameters zipParameters = new ZipParameters(); zipParameters.setEncryptFiles(true); zipParameters.setEncryptionMethod(EncryptionMethod.ZIP_STANDARD); // 必须设置压缩包内的文件名 zipParameters.setFileNameInZip(fileNameInZip); try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); ZipOutputStream zos = new ZipOutputStream(bos, password)) { zos.putNextEntry(zipParameters); zos.write(content); zos.closeEntry(); return bos.toByteArray(); } } /** * 解压带密码的zip包字节数组,返回第一个文件的内容字节数组 * @param zipBytes 压缩包字节数组 * @param password 解压密码 * @return 解压后的文件内容字节数组 */ public static byte[] extractWithPassword(byte[] zipBytes, char[] password) { byte[] buffer = new byte[4096]; int readLen; try (ByteArrayInputStream bis = new ByteArrayInputStream(zipBytes); ZipInputStream zis = new ZipInputStream(bis, password); ByteArrayOutputStream bos = new ByteArrayOutputStream()) { LocalFileHeader localFileHeader; while ((localFileHeader = zis.getNextEntry()) != null) { // 跳过目录 if (localFileHeader.isDirectory()) continue; while ((readLen = zis.read(buffer)) != -1) { bos.write(buffer, 0, readLen); } // 如果你需要取所有文件可以在这里把每个文件的bos.toByteArray()存到集合里 break; } return bos.toByteArray(); } catch (IOException e) { e.printStackTrace(); return new byte[0]; } } // 测试方法 public static void main(String[] args) { try { // 模拟待压缩的字节数组 String testContent = "这是测试压缩的内容"; byte[] originBytes = testContent.getBytes(); // 压缩 byte[] zipBytes = compressWithPassword(originBytes, "test.txt", "abc123".toCharArray()); System.out.println("压缩后字节数组长度:" + zipBytes.length); // 解压 byte[] extractBytes = extractWithPassword(zipBytes, "abc123".toCharArray()); System.out.println("解压后内容:" + new String(extractBytes)); } catch (Exception e) { e.printStackTrace(); } } }
原有代码其他问题说明
- 你原来的测试代码里
fos.write(outputArr[0])是错误写法,只会写出解压后字节数组的第一个字节,应该改为fos.write(outputArr)输出整个数组 - 原来的解压逻辑如果遇到压缩包内有目录会读空内容,修正后的代码增加了目录跳过逻辑
内容的提问来源于stack exchange,提问作者user1902124
相关产品推荐
相关产品推荐

