zip4j--處理帶密碼的zip包

注:轉自:http://blog.csdn.net/djun100/article/details/18007099java

前言


一個多月前,因項目須要對Java語言下的zip格式壓縮文件的處理做了一些瞭解,嘗試了多種開源項目並寫了幾篇博客作記錄:
算法

 ZIP4J,做爲解決了個人問題的終極解決方案,原本一開始在搜索引擎上就看到了它的蹤影,但因天朝的網絡環境問題,zip4j的官網一直沒法訪問,最終使我多走了好多冤枉路,期間試過JDK的zip包,試過Apache的zip解決方案,也試過如winzipaes等其它的開源框架,最終沒有知足本身的需求,最後,我不得已掛了一下代理將zip4j下載了下來,試用了一下,果真威力無比,所到之處所向披靡...
apache

閒話少說,若是須要能夠到zip4j的官網下載該開源項目:數組

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

不過須要提醒的是可能沒法直接訪問,若是沒法正常訪問,請自行準備代理訪問,若是各位嫌麻煩,能夠到這裏下載框架

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

這是個人CSDN資源連接,下載須要3分,您若是分數很少,能夠留言索取,呵呵...我也須要積分,請諒解!post

官網上下載的資源好像是不帶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. public static void unzip(File zipFile, String dest, String passwd) throws ZipException {    
  2.         ZipFile zFile = new ZipFile(zipFile);  // 首先建立ZipFile指向磁盤上的.zip文件    
  3.         zFile.setFileNameCharset("GBK");       // 設置文件名編碼,在GBK系統中須要設置    
  4.         if (!zFile.isValidZipFile()) {   // 驗證.zip文件是否合法,包括文件是否存在、是否爲zip文件、是否被損壞等    
  5.             throw new ZipException("壓縮文件不合法,可能被損壞.");    
  6.         }    
  7.         File destDir = new File(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. package com.ninemax.cul.util;  
  2.   
  3. import java.io.File;  
  4. import java.util.ArrayList;  
  5. import java.util.Collections;  
  6. import java.util.List;  
  7.   
  8. import org.apache.commons.lang3.StringUtils;  
  9.   
  10. import net.lingala.zip4j.core.ZipFile;  
  11. import net.lingala.zip4j.exception.ZipException;  
  12. import net.lingala.zip4j.model.FileHeader;  
  13. import net.lingala.zip4j.model.ZipParameters;  
  14. import net.lingala.zip4j.util.Zip4jConstants;  
  15.   
  16. /** 
  17.  * ZIP壓縮文件操做工具類 
  18.  * 支持密碼 
  19.  * 依賴zip4j開源項目(http://www.lingala.net/zip4j/) 
  20.  * 版本1.3.1 
  21.  * @author ninemax 
  22.  */  
  23. public class CompressUtil {  
  24.       
  25.     /** 
  26.      * 使用給定密碼解壓指定的ZIP壓縮文件到指定目錄 
  27.      * <p> 
  28.      * 若是指定目錄不存在,能夠自動建立,不合法的路徑將致使異常被拋出 
  29.      * @param zip 指定的ZIP壓縮文件 
  30.      * @param dest 解壓目錄 
  31.      * @param passwd ZIP文件的密碼 
  32.      * @return 解壓後文件數組 
  33.      * @throws ZipException 壓縮文件有損壞或者解壓縮失敗拋出 
  34.      */  
  35.     public static File [] unzip(String zip, String dest, String passwd) throws ZipException {  
  36.         File zipFile = new File(zip);  
  37.         return unzip(zipFile, dest, passwd);  
  38.     }  
  39.       
  40.     /** 
  41.      * 使用給定密碼解壓指定的ZIP壓縮文件到當前目錄 
  42.      * @param zip 指定的ZIP壓縮文件 
  43.      * @param passwd ZIP文件的密碼 
  44.      * @return  解壓後文件數組 
  45.      * @throws ZipException 壓縮文件有損壞或者解壓縮失敗拋出 
  46.      */  
  47.     public static File [] unzip(String zip, String passwd) throws ZipException {  
  48.         File zipFile = new File(zip);  
  49.         File parentDir = zipFile.getParentFile();  
  50.         return unzip(zipFile, parentDir.getAbsolutePath(), passwd);  
  51.     }  
  52.       
  53.     /** 
  54.      * 使用給定密碼解壓指定的ZIP壓縮文件到指定目錄 
  55.      * <p> 
  56.      * 若是指定目錄不存在,能夠自動建立,不合法的路徑將致使異常被拋出 
  57.      * @param zip 指定的ZIP壓縮文件 
  58.      * @param dest 解壓目錄 
  59.      * @param passwd ZIP文件的密碼 
  60.      * @return  解壓後文件數組 
  61.      * @throws ZipException 壓縮文件有損壞或者解壓縮失敗拋出 
  62.      */  
  63.     public static File [] unzip(File zipFile, String dest, String passwd) throws ZipException {  
  64.         ZipFile zFile = new ZipFile(zipFile);  
  65.         zFile.setFileNameCharset("GBK");  
  66.         if (!zFile.isValidZipFile()) {  
  67.             throw new ZipException("壓縮文件不合法,可能被損壞.");  
  68.         }  
  69.         File destDir = new File(dest);  
  70.         if (destDir.isDirectory() && !destDir.exists()) {  
  71.             destDir.mkdir();  
  72.         }  
  73.         if (zFile.isEncrypted()) {  
  74.             zFile.setPassword(passwd.toCharArray());  
  75.         }  
  76.         zFile.extractAll(dest);  
  77.           
  78.         List<FileHeader> headerList = zFile.getFileHeaders();  
  79.         List<File> extractedFileList = new ArrayList<File>();  
  80.         for(FileHeader fileHeader : headerList) {  
  81.             if (!fileHeader.isDirectory()) {  
  82.                 extractedFileList.add(new File(destDir,fileHeader.getFileName()));  
  83.             }  
  84.         }  
  85.         File [] extractedFiles = new File[extractedFileList.size()];  
  86.         extractedFileList.toArray(extractedFiles);  
  87.         return extractedFiles;  
  88.     }  
  89.       
  90.     /** 
  91.      * 壓縮指定文件到當前文件夾 
  92.      * @param src 要壓縮的指定文件 
  93.      * @return 最終的壓縮文件存放的絕對路徑,若是爲null則說明壓縮失敗. 
  94.      */  
  95.     public static String zip(String src) {  
  96.         return zip(src,null);  
  97.     }  
  98.       
  99.     /** 
  100.      * 使用給定密碼壓縮指定文件或文件夾到當前目錄 
  101.      * @param src 要壓縮的文件 
  102.      * @param passwd 壓縮使用的密碼 
  103.      * @return 最終的壓縮文件存放的絕對路徑,若是爲null則說明壓縮失敗. 
  104.      */  
  105.     public static String zip(String src, String passwd) {  
  106.         return zip(src, null, passwd);  
  107.     }  
  108.       
  109.     /** 
  110.      * 使用給定密碼壓縮指定文件或文件夾到當前目錄 
  111.      * @param src 要壓縮的文件 
  112.      * @param dest 壓縮文件存放路徑 
  113.      * @param passwd 壓縮使用的密碼 
  114.      * @return 最終的壓縮文件存放的絕對路徑,若是爲null則說明壓縮失敗. 
  115.      */  
  116.     public static String zip(String src, String dest, String passwd) {  
  117.         return zip(src, dest, true, passwd);  
  118.     }  
  119.       
  120.     /** 
  121.      * 使用給定密碼壓縮指定文件或文件夾到指定位置. 
  122.      * <p> 
  123.      * dest可傳最終壓縮文件存放的絕對路徑,也能夠傳存放目錄,也能夠傳null或者"".<br /> 
  124.      * 若是傳null或者""則將壓縮文件存放在當前目錄,即跟源文件同目錄,壓縮文件名取源文件名,以.zip爲後綴;<br /> 
  125.      * 若是以路徑分隔符(File.separator)結尾,則視爲目錄,壓縮文件名取源文件名,以.zip爲後綴,不然視爲文件名. 
  126.      * @param src 要壓縮的文件或文件夾路徑 
  127.      * @param dest 壓縮文件存放路徑 
  128.      * @param isCreateDir 是否在壓縮文件裏建立目錄,僅在壓縮文件爲目錄時有效.<br /> 
  129.      * 若是爲false,將直接壓縮目錄下文件到壓縮文件. 
  130.      * @param passwd 壓縮使用的密碼 
  131.      * @return 最終的壓縮文件存放的絕對路徑,若是爲null則說明壓縮失敗. 
  132.      */  
  133.     public static String zip(String src, String dest, boolean isCreateDir, String passwd) {  
  134.         File srcFile = new File(src);  
  135.         dest = buildDestinationZipFilePath(srcFile, dest);  
  136.         ZipParameters parameters = new ZipParameters();  
  137.         parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);           // 壓縮方式  
  138.         parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);    // 壓縮級別  
  139.         if (!StringUtils.isEmpty(passwd)) {  
  140.             parameters.setEncryptFiles(true);  
  141.             parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_STANDARD); // 加密方式  
  142.             parameters.setPassword(passwd.toCharArray());  
  143.         }  
  144.         try {  
  145.             ZipFile zipFile = new ZipFile(dest);  
  146.             if (srcFile.isDirectory()) {  
  147.                 // 若是不建立目錄的話,將直接把給定目錄下的文件壓縮到壓縮文件,即沒有目錄結構  
  148.                 if (!isCreateDir) {  
  149.                     File [] subFiles = srcFile.listFiles();  
  150.                     ArrayList<File> temp = new ArrayList<File>();  
  151.                     Collections.addAll(temp, subFiles);  
  152.                     zipFile.addFiles(temp, parameters);  
  153.                     return dest;  
  154.                 }  
  155.                 zipFile.addFolder(srcFile, parameters);  
  156.             } else {  
  157.                 zipFile.addFile(srcFile, parameters);  
  158.             }  
  159.             return dest;  
  160.         } catch (ZipException e) {  
  161.             e.printStackTrace();  
  162.         }  
  163.         return null;  
  164.     }  
  165.       
  166.     /** 
  167.      * 構建壓縮文件存放路徑,若是不存在將會建立 
  168.      * 傳入的多是文件名或者目錄,也可能不傳,此方法用以轉換最終壓縮文件的存放路徑 
  169.      * @param srcFile 源文件 
  170.      * @param destParam 壓縮目標路徑 
  171.      * @return 正確的壓縮文件存放路徑 
  172.      */  
  173.     private static String buildDestinationZipFilePath(File srcFile,String destParam) {  
  174.         if (StringUtils.isEmpty(destParam)) {  
  175.             if (srcFile.isDirectory()) {  
  176.                 destParam = srcFile.getParent() + File.separator + srcFile.getName() + ".zip";  
  177.             } else {  
  178.                 String fileName = srcFile.getName().substring(0, srcFile.getName().lastIndexOf("."));  
  179.                 destParam = srcFile.getParent() + File.separator + fileName + ".zip";  
  180.             }  
  181.         } else {  
  182.             createDestDirectoryIfNecessary(destParam);  // 在指定路徑不存在的狀況下將其建立出來  
  183.             if (destParam.endsWith(File.separator)) {  
  184.                 String fileName = "";  
  185.                 if (srcFile.isDirectory()) {  
  186.                     fileName = srcFile.getName();  
  187.                 } else {  
  188.                     fileName = srcFile.getName().substring(0, srcFile.getName().lastIndexOf("."));  
  189.                 }  
  190.                 destParam += fileName + ".zip";  
  191.             }  
  192.         }  
  193.         return destParam;  
  194.     }  
  195.       
  196.     /** 
  197.      * 在必要的狀況下建立壓縮文件存放目錄,好比指定的存放路徑並無被建立 
  198.      * @param destParam 指定的存放路徑,有可能該路徑並無被建立 
  199.      */  
  200.     private static void createDestDirectoryIfNecessary(String destParam) {  
  201.         File destDir = null;  
  202.         if (destParam.endsWith(File.separator)) {  
  203.             destDir = new File(destParam);  
  204.         } else {  
  205.             destDir = new File(destParam.substring(0, destParam.lastIndexOf(File.separator)));  
  206.         }  
  207.         if (!destDir.exists()) {  
  208.             destDir.mkdirs();  
  209.         }  
  210.     }  
  211.   
  212.     public static void main(String[] args) {  
  213.         zip("d:\\test\\cc""d:\\test\\cc.zip""11");  
  214. //      try {  
  215. //          File[] files = unzip("d:\\test\\漢字.zip", "aa");  
  216. //          for (int i = 0; i < files.length; i++) {  
  217. //              System.out.println(files[i]);  
  218. //          }  
  219. //      } catch (ZipException e) {  
  220. //          e.printStackTrace();  
  221. //      }  
  222.     }  
  223. }  

須要學習的東西太多,沒太多時間(或許只是藉口)去研究它,上面的例子僅是簡單地解壓和壓縮操做;但在使用中能夠發現Zip4J功能比較完備,若是須要更多地支持,那就真要好好去研究一下它,也許它真的不會使您失望。。。


補充


刪除壓縮文件中的目錄


看到有朋友在問如何刪除壓縮文件中的目錄,在這裏補充一下。

利用zip4j刪除壓縮文件中的目錄,查閱API後很容易想到這樣的方式:

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

但這種直接刪除壓縮文件中非空目錄的方式是不會成功的,你會看到zip文件絲毫沒有變化,雖然目錄對應的FileHeader已被刪除(表現就是若是這時再將目錄下的全部文件刪除,則該目錄隨之消失) ;所以咱們須要將該目錄下全部的文件都刪除掉,最後再將目錄刪除,根據這個思路,咱們很容易造成以下的代碼:

[java]  view plain copy
  1. void removeDirFromZipArchive(String file, String removeDir) throws ZipException {  
  2.     // 建立ZipFile並設置編碼  
  3.     ZipFile zipFile = new ZipFile(file);  
  4.     zipFile.setFileNameCharset("GBK");  
  5.       
  6.     // 給要刪除的目錄加上路徑分隔符  
  7.     if (!removeDir.endsWith(File.separator)) removeDir += File.separator;  
  8.   
  9.     // 若是目錄不存在, 直接返回  
  10.     FileHeader dirHeader = zipFile.getFileHeader(removeDir);  
  11.     if (null == dirHeader) return;  
  12.   
  13.     // 遍歷壓縮文件中全部的FileHeader, 將指定刪除目錄下的子文件刪除  
  14.     List allHeaders = zipFile.getFileHeaders();  
  15.     for(int i=0, len = allHeaders.size(); i<len; i++) {  
  16.         FileHeader subHeader = (FileHeader) allHeaders.get(i);  
  17.         if (subHeader.getFileName().startsWith(dirHeader.getFileName())  
  18.                 && !subHeader.getFileName().equals(dirHeader.getFileName())) {  
  19.             zipFile.removeFile(subHeader);  
  20.         }  
  21.     }  
  22.     // 最後刪除指定目錄  
  23.     zipFile.removeFile(dirHeader);  
  24. }  

這樣仍然解決不了問題,若是你這樣作了,那麼你將會獲得一個java.lang.IndexOutOfBoundsException異常,那麼看似正常的代碼爲何會報索引越界異常呢?其實咱們經過zipFile.getFileHeaders()方法獲得的List會隨遍歷中的刪除操做而發生變化,也就是說咱們刪除了某個FileHeader,將會反映到該List中。每成功刪除一個FileHeader,List長度就減1,而i一直在0至List的初始長度之間遞增,反覆幾回後就可能出現越界異常。

爲了不這種狀況發生,咱們能夠多作一些操做,好比能夠在遍歷中暫不進行刪除操做,而只是將要刪除的文件記錄下來,遍歷結束後再統一刪除,最後將目錄刪除,經測試,這個思路能夠解決問題。

簡單示例代碼:

[java]  view plain copy
  1. void removeDirFromZipArchive(String file, String removeDir) throws ZipException {  
  2.     // 建立ZipFile並設置編碼  
  3.     ZipFile zipFile = new ZipFile(file);  
  4.     zipFile.setFileNameCharset("GBK");  
  5.       
  6.     // 給要刪除的目錄加上路徑分隔符  
  7.     if (!removeDir.endsWith(File.separator)) removeDir += File.separator;  
  8.       
  9.     // 若是目錄不存在, 直接返回  
  10.     FileHeader dirHeader = zipFile.getFileHeader(removeDir);  
  11.     if (null == dirHeader) return;  
  12.   
  13.     // 遍歷壓縮文件中全部的FileHeader, 將指定刪除目錄下的子文件名保存起來  
  14.     List headersList = zipFile.getFileHeaders();  
  15.     List<String> removeHeaderNames = new ArrayList<String>();  
  16.     for(int i=0, len = headersList.size(); i<len; i++) {  
  17.         FileHeader subHeader = (FileHeader) headersList.get(i);  
  18.         if (subHeader.getFileName().startsWith(dirHeader.getFileName())  
  19.                 && !subHeader.getFileName().equals(dirHeader.getFileName())) {  
  20.             removeHeaderNames.add(subHeader.getFileName());  
  21.         }  
  22.     }  
  23.     // 遍歷刪除指定目錄下的全部子文件, 最後刪除指定目錄(此時已爲空目錄)  
  24.     for(String headerNameString : removeHeaderNames) {  
  25.         zipFile.removeFile(headerNameString);  
  26.     }  
  27.     zipFile.removeFile(dirHeader);  
  28. }  

也許還有其它的辦法來解決此問題,若是您有須要,就留待您來解決了。

相關文章
相關標籤/搜索