https://www.jb51.net/article/164137.htm
前言
JDK自带的ZIP操作接口(java.util.zip包,请参看文章末尾的博客链接)并不支持密码,甚至也不支持中文文件名。
为了解决ZIP压缩文件的密码问题,在网上搜索良久,终于找到了winzipaes开源项目。
该项目在google code下托管 ,仅支持AES压缩和解压zip文件( This library only supports Win-Zip's 256-Bit AES mode.)。网站上下载的文件是源代码,最新版本为winzipaes_src_20120416.zip,本示例就是在此基础上编写。
详述
项目使用很简单,利用源码自己导出一个jar文件,在项目中引用即可。
这里有一个需要注意的问题,就是如果给定ZIP文件没有密码,那么就不能使用该项目解压,如果压缩文件没有密码却使用该项目解压在这里会报一个异常,所以使用中需要注意:加密ZIP文件可以使用它解压,没有加密的就需要采取其它方式了。
此文就是采用修改后的winzipaes编写,并记录详细修改步骤。
winzipaes项目依赖bcprov的jar包
Windows命令行:
1 try{2
3 String cmd = "unzip -o -P" + passWord + nssDecomFilePath + "\\"
4 +zipFileName;5
6 Runtime.getRuntime().exec(cmd);7 } catch(Exception ex) {8 return false;9 }
示例
在研究该项目时写了一个工具类,本来准备用在项目中,最后找到了更好的解决方案zip4j来代替,所以最终没有采用。
1 packagecom.ninemax.demo.zip.decrypt;2 importjava.io.File;3 importjava.io.IOException;4 importjava.util.List;5 importjava.util.zip.DataFormatException;6 importorg.apache.commons.io.FileUtils;7 importde.idyl.winzipaes.AesZipFileDecrypter;8 importde.idyl.winzipaes.AesZipFileEncrypter;9 importde.idyl.winzipaes.impl.AESDecrypter;10 importde.idyl.winzipaes.impl.AESDecrypterBC;11 importde.idyl.winzipaes.impl.AESEncrypter;12 importde.idyl.winzipaes.impl.AESEncrypterBC;13 importde.idyl.winzipaes.impl.ExtZipEntry;14 /**
15 * 压缩指定文件或目录为ZIP格式压缩文件16 * 支持中文(修改源码后)17 * 支持密码(仅支持256bit的AES加密解密)18 * 依赖bcprov项目(bcprov-jdk16-140.jar)19 *20 *@authorzyh21 */
22 public classDecryptionZipUtil {23 /**
24 * 使用指定密码将给定文件或文件夹压缩成指定的输出ZIP文件25 *@paramsrcFile 需要压缩的文件或文件夹26 *@paramdestPath 输出路径27 *@parampasswd 压缩文件使用的密码28 */
29 public static voidzip(String srcFile,String destPath,String passwd) {30 AESEncrypter encrypter = newAESEncrypterBC();31 AesZipFileEncrypter zipFileEncrypter = null;32 try{33 zipFileEncrypter = newAesZipFileEncrypter(destPath, encrypter);34 /**
35 * 此方法是修改源码后添加,用以支持中文文件名36 */
37 zipFileEncrypter.setEncoding("utf8");38 File sFile = newFile(srcFile);39 /**
40 * AesZipFileEncrypter提供了重载的添加Entry的方法,其中:41 * add(File f, String passwd)42 * 方法是将文件直接添加进压缩文件43 *44 * add(File f, String pathForEntry, String passwd)45 * 方法是按指定路径将文件添加进压缩文件46 * pathForEntry - to be used for addition of the file (path within zip file)47 */
48 doZip(sFile, zipFileEncrypter, "", passwd);49 } catch(IOException e) {50 e.printStackTrace();51 } finally{52 try{53 zipFileEncrypter.close();54 } catch(IOException e) {55 e.printStackTrace();56 }57 }58 }59
60 /**
61 * 具体压缩方法,将给定文件添加进压缩文件中,并处理压缩文件中的路径62 *@paramfile 给定磁盘文件(是文件直接添加,是目录递归调用添加)63 *@paramencrypter AesZipFileEncrypter实例,用于输出加密ZIP文件64 *@parampathForEntry ZIP文件中的路径65 *@parampasswd 压缩密码66 *@throwsIOException67 */
68 private static voiddoZip(File file, AesZipFileEncrypter encrypter,69 String pathForEntry, String passwd) throwsIOException {70 if(file.isFile()) {71 pathForEntry +=file.getName();72 encrypter.add(file, pathForEntry, passwd);73 return;74 }75 pathForEntry += file.getName() +File.separator;76 for(File subFile : file.listFiles()) {77 doZip(subFile, encrypter, pathForEntry, passwd);78 }79 }80
81 /**
82 * 使用给定密码解压指定压缩文件到指定目录83 *@paraminFile 指定Zip文件84 *@paramoutDir 解压目录85 *@parampasswd 解压密码86 */
87 public static voidunzip(String inFile, String outDir, String passwd) {88 File outDirectory = newFile(outDir);89 if (!outDirectory.exists()) {90 outDirectory.mkdir();91 }92 AESDecrypter decrypter = newAESDecrypterBC();93 AesZipFileDecrypter zipDecrypter = null;94 try{95 zipDecrypter = new AesZipFileDecrypter(newFile(inFile), decrypter);96 AesZipFileDecrypter.charset = "utf-8";97 /**
98 * 得到ZIP文件中所有Entry,但此处好像与JDK里不同,目录不视为Entry99 * 需要创建文件夹,entry.isDirectory()方法同样不适用,不知道是不是自己使用错误100 * 处理文件夹问题处理可能不太好101 */
102 List entryList =zipDecrypter.getEntryList();103 for(ExtZipEntry entry : entryList) {104 String eName =entry.getName();105 String dir = eName.substring(0, eName.lastIndexOf(File.separator) + 1);106 File extractDir = newFile(outDir, dir);107 if (!extractDir.exists()) {108 FileUtils.forceMkdir(extractDir);109 }110 /**
111 * 抽出文件112 */
113 File extractFile = new File(outDir + File.separator +eName);114 zipDecrypter.extractEntry(entry, extractFile, passwd);115 }116 } catch(IOException e) {117 e.printStackTrace();118 } catch(DataFormatException e) {119 e.printStackTrace();120 } finally{121 try{122 zipDecrypter.close();123 } catch(IOException e) {124 e.printStackTrace();125 }126 }127 }128 /**
129 * 测试130 *@paramargs131 */
132 public static voidmain(String[] args) {133 /**
134 * 压缩测试135 * 可以传文件或者目录136 */
137 //zip("M:\\ZIP\\test\\bb\\a\\t.txt", "M:\\ZIP\\test\\temp1.zip", "zyh");138 //zip("M:\\ZIP\\test\\bb", "M:\\ZIP\\test\\temp2.zip", "zyh");
139 unzip("M:\\ZIP\\test\\temp2.zip", "M:\\ZIP\\test\\temp", "zyh");140 }141 }
压缩多个文件时,有两个方法(第一种没试):
(1) 预先把多个文件压缩成zip,然后调用enc.addAll(inZipFile, password);方法将多个zip文件加进来。
(2)针对需要压缩的文件循环调用enc.add(inFile, password);,每次都用相同的密码。
修改源码后的项目可到上面提到的博客去下载,或者参照博客自己修改,其实也很容易,毕竟只有几处改动。
另外我的CSDN下载频道也上传了修改后的源码和jar包,也可以去那里下载。
修改记录
需要修改的文件有:
ExtZipOutputStream
ExtZipEntry
AesZipFileEncrypter
在ExtZipOutputStream里增加一成员变量并添加两个方法:
1 protected String encoding = "iso-8859-1";2 public boolean utf8Flg = false;3 public voidsetEncoding(String encoding) {4 this.encoding =encoding;5 utf8Flg |=isUTF8(encoding);6 }7 protected booleanisUTF8(String encoding) {8 if (encoding == null) {9 //check platform's default encoding
10 encoding = System.getProperty("file.encoding");11 }12 return "UTF8".equalsIgnoreCase(encoding)13 || "UTF-8".equalsIgnoreCase(encoding);14 }
然后将ExtZipOutputStream的(134行和158行左右)iso-8859-1编码替换成上面设置的编码格式
接着,再将106行左右文件名长度取得代码改成:
writeShort(entry.getName().getBytes(encoding).length); //file name length
这里有个地方需要注意,当文件名是utf8编码格式的时候,需要设置Zip包的通用位标志 (不明白)
第十一个比特为1,代码修改如下:
修改ExtZipEntry类在initEncryptedEntry方法基础上增加一个重载方法:
1 public void initEncryptedEntry(booleanutf8Flag) {2 setCrc(0); //CRC-32 / for encrypted files it's 0 as AES/MAC checks integritiy
3 this.flag |= 1; //bit0 - encrypted
4 if(utf8Flag) {5 this.flag |=(1 << 11);6 }7 //flag |= 8;//bit3 - use data descriptor
8 this.primaryCompressionMethod = 0x63;9
10 byte[] extraBytes = new byte[11];11 extraBytes = new byte[11];12 //extra data header ID for AES encryption is 0x9901
13 extraBytes[0] = 0x01;14 extraBytes[1] = (byte)0x99;15 //data size (currently 7, but subject to possible increase in the16 //future)
17 extraBytes[2] = 0x07; //data size
18 extraBytes[3] = 0x00; //data size19 //Integer version number specific to the zip vendor
20 extraBytes[4] = 0x02; //version number
21 extraBytes[5] = 0x00; //version number22
23 //2-character vendor ID
24 extraBytes[6] = 0x41; //vendor id
25 extraBytes[7] = 0x45; //vendor id26 //AES encryption strength - 1=128, 2=192, 3=256
27 extraBytes[8] = 0x03;28 //actual compression method - 0x0000==stored (no compression) - 2 bytes
29 extraBytes[9] = (byte) (getMethod() & 0xff);30 extraBytes[10] = (byte) ((getMethod() & 0xff00) >> 8);31
32 setExtra(extraBytes);33 }
其实就是增加一个参数并增加了下面这段代码:
1 if(utf8Flag) {2 this.flag |=(1 << 11);3 }
当然不要忘了将调用该方法地方修改一下,传进utf8Flag参数
AesZipFileEncrypter类里有两处(在两个add方法中)其它地方不需改动。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。