zip4j -- Java处理zip压缩文件的完整解决方案

前言


一个多月前,因项目需要对Java语言下的zip格式压缩文件的处理作了一些了解,尝试了多种开源项目并写了几篇博客做记录:

ZIP4J,作为解决了我的问题的终极解决方案,本来一开始在搜索引擎上就看到了它的踪迹,但因天朝的网络环境问题,zip4j的官网一直无法访问,最终使我多走了好多冤枉路,期间试过JDK的zip包,试过Apache的zip解决方案,也试过如winzipaes等其它的开源框架,最终没有满足自己的需求,最后,我不得已挂了一下代理将zip4j下载了下来,试用了一下,果然威力无比,所到之处所向披靡...

闲话少说,如果需要可以到zip4j的官网下载该开源项目:

http://www.lingala.net/zip4j/

不过需要提醒的是可能无法直接访问,如果无法正常访问,请自行准备代理访问,如果各位嫌麻烦,可以到这里下载

http://download.csdn.net/detail/zhangyihui1986/4418509

这是我的CSDN资源链接,下载需要3分,您如果分数不多,可以留言索取,呵呵...我也需要积分,请谅解!

官网上下载的资源好像是不带API帮助文档的,我利用其源码生成了一份,也一并打在我的资源文件中,希望能帮到大家。


ZIP4J的官方说明


(自己翻译了一下,英文不好,呵呵...)

Key features(主要特性):

  • Create, Add, Extract, Update, Remove files from a Zip file
    针对ZIP压缩文件创建、添加、抽出、更新和移除文件
  • Read/Write password protected Zip files
    (读写有密码保护的Zip文件)
  • Supports AES 128/256 Encryption
    (支持AES 128/256算法加密)
  • Supports Standard Zip Encryption
    (支持标准Zip算法加密)
  • Supports Zip64 format
    (支持zip64格式)
  • Supports Store (No Compression) and Deflate compression method
    (支持Store(非压缩)和Deflate压缩方法---不太明白)
  • Create or extract files from Split Zip files (Ex: z01, z02,...zip)
    (针对分块zip文件创建和抽出文件)
  • Supports Unicode file names
    (支持Unicode编码文件名)
  • Progress Monitor
    (进度监控)

从上面的主要特性可以看出,zip4j的功能是非常强大的,完全可以利用其写个类似于好压的zip文件管理软件,但我们用地最多的可能还是利用其作一些简单的解压和压缩操作,其它的功能极少触碰,我也一样,呵呵...


使用注意点


zip4j默认采用UTF-8编码,所以它支持中文,同时也支持密码,而且支持多种压缩算法,可以说功能强大,但使用起来却非常简单,当然,如果需求比较复杂,那就得好好去研究了。如果你仅仅是简单地解压一个zip压缩文件,那么只需要简单地几步即可:

[java] view plain copy
  1. publicstaticvoidunzip(FilezipFile,Stringdest,Stringpasswd)throwsZipException{
  2. ZipFilezFile=newZipFile(zipFile);//首先创建ZipFile指向磁盘上的.zip文件
  3. zFile.setFileNameCharset("GBK");//设置文件名编码,在GBK系统中需要设置
  4. if(!zFile.isValidZipFile()){//验证.zip文件是否合法,包括文件是否存在、是否为zip文件、是否被损坏等
  5. thrownewZipException("压缩文件不合法,可能被损坏.");
  6. }
  7. FiledestDir=newFile(dest);//解压目录
  8. if(destDir.isDirectory()&&!destDir.exists()){
  9. destDir.mkdir();
  10. }
  11. if(zFile.isEncrypted()){
  12. zFile.setPassword(passwd.toCharArray());//设置密码
  13. }
  14. zFile.extractAll(dest);//将文件抽出到解压目录(解压)
  15. }

当然将指定文件压缩成zip文件也是非常简单的事,此处不再贴代码,如有需要请参看下面的完整示例。

提示:如果将要解压的压缩文件中的文件名含有中文,解压时需要注意一点,就是设置文件名字符集方法

[java] view plain copy
  1. zFile.setFileNameCharset("GBK");

这个方法的调用一定要靠前,要靠多前呢?其实最好在创建ZipFile之后就设置上,至少要在

[java] view plain copy
  1. zFile.isValidZipFile()

这个方法调用之前调用,我在应用时因为这个问题耽误好久,最后查看源码才弄明白,好像是ZipFile在验证方法中就将编码设置好,以后就不再对文件名编码作改变了。


完整示例


下面提供一个自己写的例子,鄙人才疏学浅,天分也差,写的代码质量很差,斗胆贴上,希望能起到抛砖引玉的作用。

[java] view plain copy
  1. packagecom.ninemax.cul.util;
  2. importjava.io.File;
  3. importjava.util.ArrayList;
  4. importjava.util.Collections;
  5. importjava.util.List;
  6. importorg.apache.commons.lang3.StringUtils;
  7. importnet.lingala.zip4j.core.ZipFile;
  8. importnet.lingala.zip4j.exception.ZipException;
  9. importnet.lingala.zip4j.model.FileHeader;
  10. importnet.lingala.zip4j.model.ZipParameters;
  11. importnet.lingala.zip4j.util.Zip4jConstants;
  12. /**
  13. *ZIP压缩文件操作工具类
  14. *支持密码
  15. *依赖zip4j开源项目(http://www.lingala.net/zip4j/)
  16. *版本1.3.1
  17. *@authorninemax
  18. */
  19. publicclassCompressUtil{
  20. /**
  21. *使用给定密码解压指定的ZIP压缩文件到指定目录
  22. *<p>
  23. *如果指定目录不存在,可以自动创建,不合法的路径将导致异常被抛出
  24. *@paramzip指定的ZIP压缩文件
  25. *@paramdest解压目录
  26. *@parampasswdZIP文件的密码
  27. *@return解压后文件数组
  28. *@throwsZipException压缩文件有损坏或者解压缩失败抛出
  29. */
  30. publicstaticFile[]unzip(Stringzip,Stringdest,Stringpasswd)throwsZipException{
  31. FilezipFile=newFile(zip);
  32. returnunzip(zipFile,dest,passwd);
  33. }
  34. /**
  35. *使用给定密码解压指定的ZIP压缩文件到当前目录
  36. *@paramzip指定的ZIP压缩文件
  37. *@parampasswdZIP文件的密码
  38. *@return解压后文件数组
  39. *@throwsZipException压缩文件有损坏或者解压缩失败抛出
  40. */
  41. publicstaticFile[]unzip(Stringzip,Stringpasswd)throwsZipException{
  42. FilezipFile=newFile(zip);
  43. FileparentDir=zipFile.getParentFile();
  44. returnunzip(zipFile,parentDir.getAbsolutePath(),passwd);
  45. }
  46. /**
  47. *使用给定密码解压指定的ZIP压缩文件到指定目录
  48. *<p>
  49. *如果指定目录不存在,可以自动创建,不合法的路径将导致异常被抛出
  50. *@paramzip指定的ZIP压缩文件
  51. *@paramdest解压目录
  52. *@parampasswdZIP文件的密码
  53. *@return解压后文件数组
  54. *@throwsZipException压缩文件有损坏或者解压缩失败抛出
  55. */
  56. publicstaticFile[]unzip(FilezipFile,Stringdest,Stringpasswd)throwsZipException{
  57. ZipFilezFile=newZipFile(zipFile);
  58. zFile.setFileNameCharset("GBK");
  59. if(!zFile.isValidZipFile()){
  60. thrownewZipException("压缩文件不合法,可能被损坏.");
  61. }
  62. FiledestDir=newFile(dest);
  63. if(destDir.isDirectory()&&!destDir.exists()){
  64. destDir.mkdir();
  65. }
  66. if(zFile.isEncrypted()){
  67. zFile.setPassword(passwd.toCharArray());
  68. }
  69. zFile.extractAll(dest);
  70. List<FileHeader>headerList=zFile.getFileHeaders();
  71. List<File>extractedFileList=newArrayList<File>();
  72. for(FileHeaderfileHeader:headerList){
  73. if(!fileHeader.isDirectory()){
  74. extractedFileList.add(newFile(destDir,fileHeader.getFileName()));
  75. }
  76. }
  77. File[]extractedFiles=newFile[extractedFileList.size()];
  78. extractedFileList.toArray(extractedFiles);
  79. returnextractedFiles;
  80. }
  81. /**
  82. *压缩指定文件到当前文件夹
  83. *@paramsrc要压缩的指定文件
  84. *@return最终的压缩文件存放的绝对路径,如果为null则说明压缩失败.
  85. */
  86. publicstaticStringzip(Stringsrc){
  87. returnzip(src,null);
  88. }
  89. /**
  90. *使用给定密码压缩指定文件或文件夹到当前目录
  91. *@paramsrc要压缩的文件
  92. *@parampasswd压缩使用的密码
  93. *@return最终的压缩文件存放的绝对路径,如果为null则说明压缩失败.
  94. */
  95. publicstaticStringzip(Stringsrc,Stringpasswd){
  96. returnzip(src,null,passwd);
  97. }
  98. /**
  99. *使用给定密码压缩指定文件或文件夹到当前目录
  100. *@paramsrc要压缩的文件
  101. *@paramdest压缩文件存放路径
  102. *@parampasswd压缩使用的密码
  103. *@return最终的压缩文件存放的绝对路径,如果为null则说明压缩失败.
  104. */
  105. publicstaticStringzip(Stringsrc,Stringdest,Stringpasswd){
  106. returnzip(src,dest,true,passwd);
  107. }
  108. /**
  109. *使用给定密码压缩指定文件或文件夹到指定位置.
  110. *<p>
  111. *dest可传最终压缩文件存放的绝对路径,也可以传存放目录,也可以传null或者"".<br/>
  112. *如果传null或者""则将压缩文件存放在当前目录,即跟源文件同目录,压缩文件名取源文件名,以.zip为后缀;<br/>
  113. *如果以路径分隔符(File.separator)结尾,则视为目录,压缩文件名取源文件名,以.zip为后缀,否则视为文件名.
  114. *@paramsrc要压缩的文件或文件夹路径
  115. *@paramdest压缩文件存放路径
  116. *@paramisCreateDir是否在压缩文件里创建目录,仅在压缩文件为目录时有效.<br/>
  117. *如果为false,将直接压缩目录下文件到压缩文件.
  118. *@parampasswd压缩使用的密码
  119. *@return最终的压缩文件存放的绝对路径,如果为null则说明压缩失败.
  120. */
  121. publicstaticStringzip(Stringsrc,Stringdest,booleanisCreateDir,Stringpasswd){
  122. FilesrcFile=newFile(src);
  123. dest=buildDestinationZipFilePath(srcFile,dest);
  124. ZipParametersparameters=newZipParameters();
  125. parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);//压缩方式
  126. parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);//压缩级别
  127. if(!StringUtils.isEmpty(passwd)){
  128. parameters.setEncryptFiles(true);
  129. parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_STANDARD);//加密方式
  130. parameters.setPassword(passwd.toCharArray());
  131. }
  132. try{
  133. ZipFilezipFile=newZipFile(dest);
  134. if(srcFile.isDirectory()){
  135. //如果不创建目录的话,将直接把给定目录下的文件压缩到压缩文件,即没有目录结构
  136. if(!isCreateDir){
  137. File[]subFiles=srcFile.listFiles();
  138. ArrayList<File>temp=newArrayList<File>();
  139. Collections.addAll(temp,subFiles);
  140. zipFile.addFiles(temp,parameters);
  141. returndest;
  142. }
  143. zipFile.addFolder(srcFile,parameters);
  144. }else{
  145. zipFile.addFile(srcFile,parameters);
  146. }
  147. returndest;
  148. }catch(ZipExceptione){
  149. e.printStackTrace();
  150. }
  151. returnnull;
  152. }
  153. /**
  154. *构建压缩文件存放路径,如果不存在将会创建
  155. *传入的可能是文件名或者目录,也可能不传,此方法用以转换最终压缩文件的存放路径
  156. *@paramsrcFile源文件
  157. *@paramdestParam压缩目标路径
  158. *@return正确的压缩文件存放路径
  159. */
  160. privatestaticStringbuildDestinationZipFilePath(FilesrcFile,StringdestParam){
  161. if(StringUtils.isEmpty(destParam)){
  162. if(srcFile.isDirectory()){
  163. destParam=srcFile.getParent()+File.separator+srcFile.getName()+".zip";
  164. }else{
  165. StringfileName=srcFile.getName().substring(0,srcFile.getName().lastIndexOf("."));
  166. destParam=srcFile.getParent()+File.separator+fileName+".zip";
  167. }
  168. }else{
  169. createDestDirectoryIfNecessary(destParam);//在指定路径不存在的情况下将其创建出来
  170. if(destParam.endsWith(File.separator)){
  171. StringfileName="";
  172. if(srcFile.isDirectory()){
  173. fileName=srcFile.getName();
  174. }else{
  175. fileName=srcFile.getName().substring(0,srcFile.getName().lastIndexOf("."));
  176. }
  177. destParam+=fileName+".zip";
  178. }
  179. }
  180. returndestParam;
  181. }
  182. /**
  183. *在必要的情况下创建压缩文件存放目录,比如指定的存放路径并没有被创建
  184. *@paramdestParam指定的存放路径,有可能该路径并没有被创建
  185. */
  186. privatestaticvoidcreateDestDirectoryIfNecessary(StringdestParam){
  187. FiledestDir=null;
  188. if(destParam.endsWith(File.separator)){
  189. destDir=newFile(destParam);
  190. }else{
  191. destDir=newFile(destParam.substring(0,destParam.lastIndexOf(File.separator)));
  192. }
  193. if(!destDir.exists()){
  194. destDir.mkdirs();
  195. }
  196. }
  197. publicstaticvoidmain(String[]args){
  198. zip("d:\\test\\cc","d:\\test\\cc.zip","11");
  199. //try{
  200. //File[]files=unzip("d:\\test\\汉字.zip","aa");
  201. //for(inti=0;i<files.length;i++){
  202. //System.out.println(files[i]);
  203. //}
  204. //}catch(ZipExceptione){
  205. //e.printStackTrace();
  206. //}
  207. }
  208. }

需要学习的东西太多,没太多时间(或许只是借口)去研究它,上面的例子仅是简单地解压和压缩操作;但在使用中可以发现Zip4J功能比较完备,如果需要更多地支持,那就真要好好去研究一下它,也许它真的不会使您失望。。。


补充


删除压缩文件中的目录


看到有朋友在问如何删除压缩文件中的目录,在这里补充一下。

利用zip4j删除压缩文件中的目录,查阅API后很容易想到这样的方式:

[java] view plain copy
  1. ZipFilezipFile=newZipFile("d:\\FeiQ-V2.5.zip");
  2. zipFile.setFileNameCharset("GBK");
  3. zipFile.removeFile("sounds/");//sounds是zip文件中的一个目录

但这种直接删除压缩文件中非空目录的方式是不会成功的,你会看到zip文件丝毫没有变化,虽然目录对应的FileHeader已被删除(表现就是如果这时再将目录下的所有文件删除,则该目录随之消失) ;因此我们需要将该目录下所有的文件都删除掉,最后再将目录删除,根据这个思路,我们很容易形成如下的代码:

[java] view plain copy
  1. voidremoveDirFromZipArchive(Stringfile,StringremoveDir)throwsZipException{
  2. //创建ZipFile并设置编码
  3. ZipFilezipFile=newZipFile(file);
  4. zipFile.setFileNameCharset("GBK");
  5. //给要删除的目录加上路径分隔符
  6. if(!removeDir.endsWith(File.separator))removeDir+=File.separator;
  7. //如果目录不存在,直接返回
  8. FileHeaderdirHeader=zipFile.getFileHeader(removeDir);
  9. if(null==dirHeader)return;
  10. //遍历压缩文件中所有的FileHeader,将指定删除目录下的子文件删除
  11. ListallHeaders=zipFile.getFileHeaders();
  12. for(inti=0,len=allHeaders.size();i<len;i++){
  13. FileHeadersubHeader=(FileHeader)allHeaders.get(i);
  14. if(subHeader.getFileName().startsWith(dirHeader.getFileName())
  15. &&!subHeader.getFileName().equals(dirHeader.getFileName())){
  16. zipFile.removeFile(subHeader);
  17. }
  18. }
  19. //最后删除指定目录
  20. zipFile.removeFile(dirHeader);
  21. }

这样仍然解决不了问题,如果你这样做了,那么你将会得到一个java.lang.IndexOutOfBoundsException异常,那么看似正常的代码为什么会报索引越界异常呢?其实我们通过zipFile.getFileHeaders()方法得到的List会随遍历中的删除操作而发生变化,也就是说我们删除了某个FileHeader,将会反映到该List中。每成功删除一个FileHeader,List长度就减1,而i一直在0至List的初始长度之间递增,反复几次后就可能出现越界异常。

为了避免这种情况发生,我们可以多做一些操作,比如可以在遍历中暂不进行删除操作,而只是将要删除的文件记录下来,遍历结束后再统一删除,最后将目录删除,经测试,这个思路可以解决问题。

简单示例代码:

[java] view plain copy
  1. voidremoveDirFromZipArchive(Stringfile,StringremoveDir)throwsZipException{
  2. //创建ZipFile并设置编码
  3. ZipFilezipFile=newZipFile(file);
  4. zipFile.setFileNameCharset("GBK");
  5. //给要删除的目录加上路径分隔符
  6. if(!removeDir.endsWith(File.separator))removeDir+=File.separator;
  7. //如果目录不存在,直接返回
  8. FileHeaderdirHeader=zipFile.getFileHeader(removeDir);
  9. if(null==dirHeader)return;
  10. //遍历压缩文件中所有的FileHeader,将指定删除目录下的子文件名保存起来
  11. ListheadersList=zipFile.getFileHeaders();
  12. List<String>removeHeaderNames=newArrayList<String>();
  13. for(inti=0,len=headersList.size();i<len;i++){
  14. FileHeadersubHeader=(FileHeader)headersList.get(i);
  15. if(subHeader.getFileName().startsWith(dirHeader.getFileName())
  16. &&!subHeader.getFileName().equals(dirHeader.getFileName())){
  17. removeHeaderNames.add(subHeader.getFileName());
  18. }
  19. }
  20. //遍历删除指定目录下的所有子文件,最后删除指定目录(此时已为空目录)
  21. for(StringheaderNameString:removeHeaderNames){
  22. zipFile.removeFile(headerNameString);
  23. }
  24. zipFile.removeFile(dirHeader);
  25. }

也许还有其它的办法来解决此问题,如果您有需要,就留待您来解决了。

转自:http://blog.csdn.net/zhyh1986/article/details/7921376

压缩文件方法 该方法需要引用zip4j的jar文件 单个文件、多个文件压缩 /** * 使用给定密码压缩指定文件文件夹到指定位置. * * dest可传最终压缩文件存放的绝对路径,也可以传存放目录,也可以传null或者"". * 如果传null或者""则将压缩文件存放在当前目录,即跟源文件同目录,压缩文件名取源文件名,以.zip为后缀; * 如果以路径分隔符(File.separator)结尾,则视为目录,压缩文件名取源文件名,以.zip为后缀,否则视为文件名. * @param src 要压缩文件文件夹路径 * @param dest 压缩文件存放路径 * @param isCreateDir 是否在压缩文件里创建目录,仅在压缩文件为目录时有效. * 如果为false,将直接压缩目录下文件压缩文件. * @param passwd 压缩使用的密码 * @return 最终的压缩文件存放的绝对路径,如果为null则说明压缩失败. */ 方法详细见文件! 可选择文件list压缩 /** * 使用给定密码压缩指定文件list * dest可传最终压缩文件存放的绝对路径,也可以传存放目录,也可以传null或者"". * 如果传null或者""则将压缩文件存放在当前目录,即跟源文件同目录,压缩文件名取源文件名,以.zip为后缀; * 如果以路径分隔符(File.separator)结尾,则视为目录,压缩文件名取源文件名,以.zip为后缀,否则视为文件名. * @param src 要压缩文件集合 * @param dest 压缩文件存放路径 * @param isCreateDir 是否在压缩文件里创建目录,仅在压缩文件为目录时有效. * 如果为false,将直接压缩目录下文件压缩文件. * @param passwd 压缩使用的密码 * @return 最终的压缩文件存放的绝对路径,如果为null则说明压缩失败. */ 方法详细见文件! 解压 /** * 使用给定密码解压指定的ZIP压缩文件到指定目录 * * 如果指定目录不存在,可以自动创建,不合法的路径将导致异常被抛出 * @param zipFile 指定的ZIP压缩文件 * @param dest 解压目录 * @param passwd ZIP文件的密码 * @return 解压后文件数组 * @throws ZipException 压缩文件有损坏或者解压缩失败抛出 */ 方法详细见文件! 一个简单的demo 欢迎大家指点,一起提升
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值