最近接到需求需要为打包后的zip
文件设置压缩密码,而我们之前使用的ZipOutputStream
结合hutool
的NoiUtil
进行打包生成zip
文件,但是ZipOutputStream
没有设置解压密码的功能,故尝试寻找其他三方库(这里是直接问chatGPT
),它的回答是Zip4j
。
介绍 Zip4j
是一个用于处理ZIP
文件格式的Java
库,它提供了一个易于使用的API
,可以方便地创建、提取和修改ZIP
文件,以及支持密码保护的ZIP文件。
Zip4j
的主要特点包括:
支持标准ZIP
文件格式以及ZIP64
扩展格式,可以处理包含数百万文件的ZIP
文件。
支持密码保护的ZIP
文件,包括标准密码和AES
加密密码。
支持ZIP
文件的分卷(spanned)和自解压缩(self-extracting)功能。
支持ZIP
文件的压缩级别、注释和UTF-8
编码文件名等属性的设置。
提供了方便的API
,可以方便地添加、提取和修改ZIP
文件中的文件和文件夹,以及获取ZIP
文件中的文件列表和属性信息。
使用 1 2 3 4 5 <dependency> <groupId>net.lingala.zip4j</groupId> <artifactId>zip4j</artifactId> <version>2.6.1</version> </dependency>
我使用的是2.1.6
版本,新版本对旧版本中的一些常量和接口有调整。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public void firstDemo (File file1, File file2) throws ZipException { List<File> fileToAdds = Arrays.asList(file1, file2); ZipFile zipFile = new ZipFile("/xxx/result.zip" ); zipFile.addFiles(fileToAdds); } public static void main (String[] args) throws ZipException { String filePath1 = "/xxx/demo1.txt" ; String filePath2 = "/xxx/demo2.txt" ; File file1 = new File(filePath1); File file2 = new File(filePath2); ZipDemo zipDemo = new ZipDemo(); zipDemo.firstDemo(file1, file2); }
将fileToAdds
列表打包为目标地址为/xxx/result.zip
的压缩文件。这种写法是最简单的打包方式,将需要打包的源文件都打包在目标zip
文件的顶层目录下。
1 2 3 result.zip ├─────── demo1.txt └─────── demo2.txt
但是有时候,甚至大部分时候都是需要将打包源文件进行分级(压缩包内文件夹)的方式进行打包。
1 2 3 4 result.zip ├───── dir1 │ └─ demo1.txt └───── demo2.txt
1.设置目标zip
文件内文件夹结构示例代码:
1 2 3 4 5 6 7 8 9 10 11 public void customInnerFilePath (File file1, File file2) throws ZipException { ZipParameters zipParameters = new ZipParameters(); zipParameters.setFileNameInZip("dir1/demo1.txt" ); ZipFile zipFile = new ZipFile("/xxx/result.zip" ); zipFile.addFile(file1, zipParameters); zipFile.addFile(file2); }
2.设置文件夹加密:
1 2 3 4 5 6 7 8 9 10 11 12 public void encryptZip (File file1, File file2) throws ZipException { ZipParameters zipParameters = new ZipParameters(); zipParameters.setFileNameInZip("dir1/demo1.txt" ); zipParameters.setEncryptFiles(true ); zipParameters.setEncryptionMethod(EncryptionMethod.AES); zipParameters.setAesKeyStrength(AesKeyStrength.KEY_STRENGTH_256); ZipFile zipFile = new ZipFile("/xxx/result.zip" , "123456" .toCharArray()); zipFile.addFile(file1, zipParameters); zipFile.addFile(file2); }
注意: 上述代码中仅仅是对file1
设置了zipParameters
参数,也就是说现在有两个被打包文件,但是压缩参数只设置到了其中的一个文件上,打开压缩后的文件会发现,是需要解压密码才能解压,但是如果你有一些不需要解压可以查看zip
包内部情况的工具可以发现,可以file2
所对应的文件是无需密码就可以查看,也就是说,如果要对压缩包内所有文件在没有解压密码之前都不能访问,需要每个文件添加时设置zipParameters
参数,设置为加密开启。
还有一点:踩过一个坑,由于我在打包文件的时候,可能出现某一个压缩包已经打包过了,但是业务上并不知道已经打包过了,所以会出现重复打包的情况,而我在针对重复打包的情况,会先将目标zip
设置为一个空文件。
1 2 Files.newByteChannel(result, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE).close();
但是这种情况下运行会报错:
Zip file size less than minimum expected zip file size. Probably not a zip file or a corrupted zip file
1 2 3 4 if (zip4jRaf.length() < ENDHDR) { throw new ZipException("Zip file size less than minimum expected zip file size. " + "Probably not a zip file or a corrupted zip file" ); }
大概意思是目标文件是一个空文件,校验了目标文件头部的一个什么长度。没有细究这个问题,我的做法是改成直接删除旧文件,然后再生成文件。
3.打包分卷
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public static List<File> zip (List<String> srcFiles, String destFile, long fileSize) throws ZipException { ZipFile zipFile = new ZipFile(destFile); List<File> filesToAdd = srcFiles.stream().map(File::new ).collect(Collectors.toList()); ZipParameters parameters = new ZipParameters(); parameters.setCompressionMethod(CompressionMethod.DEFLATE); parameters.setCompressionLevel(CompressionLevel.NORMAL); if (fileSize==0 ){ fileSize = 65536 ; } zipFile.createSplitZipFile(filesToAdd, parameters, true , fileSize); return zipFile.getSplitZipFiles(); }
通过分卷生成的文件结构为:
1 2 3 目标文件目录 ├─────── result.z01 └─────── result.z02
这里会生成.z
+数字排列
的压缩包,要提取或打开这样的分卷ZIP
文件,必须将所有的ZIP
文件都放在同一个目录下。这种场景就可以用于大文件分卷为多个小文件后,传输小文件到目标地址。
4.提取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public void get () throws ZipException { ZipFile zipFile = new ZipFile("/xxx/result.zip" ); if (zipFile.isEncrypted()) { zipFile.setPassword("password" .toCharArray()); } zipFile.extractAll("./" ); zipFile.extractFile("demo1.txt" , "./" ); zipFile.extractFile("dir3/demo3.txt" , "./" ); zipFile.extractFile("dir3/" , "./" ); }
总之,Zip4j
是一个非常实用的Java
库,可以帮助程序员方便地处理ZIP
文件格式,节省大量的开发时间和工作量。还包含其他的一些,比如添加某个文件到zip
文件中,修改zip
中的某个文件等等。