Java壓縮技術(二) ZIP壓縮——Java原生實現

查過相關資料後才知道,ZIP應該算作歸檔類的壓縮算法,每一門學科都可深可淺! 

閒言少敘,先說ZIP壓縮。 
zip壓縮需要通過ZipOutputStream 執行write方法將壓縮數據寫到指定輸出流中。 
注意,這裏應先使用CheckedOutputStream 指定文件校驗算法。(通常使用CRC32算法)。代碼如下所示: 
Java代碼  收藏代碼
  1. CheckedOutputStream cos = new CheckedOutputStream(new FileOutputStream(destPath), new CRC32());  
  2. ZipOutputStream zos = new ZipOutputStream(cos);  

接下來,需要將待壓縮文件以ZipEntry的方式追加到壓縮文件中,如下所示: 
Java代碼  收藏代碼
  1.  /** 
  2.  * 壓縮包內文件名定義 
  3.  *  
  4.  * <pre> 
  5.  * 如果有多級目錄,那麼這裏就需要給出包含目錄的文件名 
  6.  * 如果用WinRAR打開壓縮包,中文名將顯示爲亂碼 
  7.  * </pre> 
  8.  */  
  9. ZipEntry entry = new ZipEntry(dir + file.getName());  
  10.   
  11. zos.putNextEntry(entry);  

ZipEntry就是壓縮包中的每一個實體! 
完成上述準備後,就可以執行壓縮操作了。實際上,就是執行ZipOutputStream類的write方法,如下所示: 
Java代碼  收藏代碼
  1. BufferedInputStream bis = new BufferedInputStream(new FileInputStream(  
  2.         file));  
  3.   
  4. int count;  
  5. byte data[] = new byte[BUFFER];  
  6. while ((count = bis.read(data, 0, BUFFER)) != -1) {  
  7.     zos.write(data, 0, count);  
  8. }  
  9. bis.close();  

當然,如果待添加的壓縮項是一個目錄。那麼,需要通過遞歸的方式指定最終的壓縮項。 
如果要添加一個空目錄,注意使用符號"/"(String PATH="/";)作爲添加項名字結尾符! 

遞歸構建目錄壓縮,代碼如下: 
Java代碼  收藏代碼
  1. /** 
  2.  * 壓縮 
  3.  *  
  4.  * @param srcFile 
  5.  *            源路徑 
  6.  * @param zos 
  7.  *            ZipOutputStream 
  8.  * @param basePath 
  9.  *            壓縮包內相對路徑 
  10.  * @throws Exception 
  11.  */  
  12. private static void compress(File srcFile, ZipOutputStream zos,  
  13.         String basePath) throws Exception {  
  14.     if (srcFile.isDirectory()) {  
  15.         compressDir(srcFile, zos, basePath);  
  16.     } else {  
  17.         compressFile(srcFile, zos, basePath);  
  18.     }  
  19. }  
  20.   
  21. /** 
  22.  * 壓縮目錄 
  23.  *  
  24.  * @param dir 
  25.  * @param zos 
  26.  * @param basePath 
  27.  * @throws Exception 
  28.  */  
  29. private static void compressDir(File dir, ZipOutputStream zos,  
  30.         String basePath) throws Exception {  
  31.   
  32.     File[] files = dir.listFiles();  
  33.   
  34.     // 構建空目錄  
  35.     if (files.length < 1) {  
  36.         ZipEntry entry = new ZipEntry(basePath + dir.getName() + PATH);  
  37.   
  38.         zos.putNextEntry(entry);  
  39.         zos.closeEntry();  
  40.     }  
  41.   
  42.     for (File file : files) {  
  43.         // 遞歸壓縮  
  44.         compress(file, zos, basePath + dir.getName() + PATH);  
  45.     }  
  46. }  

x是一個空目錄,用WinRAR打開後,可以看到這個目錄下還有一個空文件名文件! 
 

來個完整的壓縮實現,代碼如下所示: 
Java代碼  收藏代碼
  1. /** 
  2.  * 2010-4-12 
  3.  */  
  4. package org.zlex.commons.io;  
  5.   
  6. import java.io.BufferedInputStream;  
  7. import java.io.BufferedOutputStream;  
  8. import java.io.File;  
  9. import java.io.FileInputStream;  
  10. import java.io.FileOutputStream;  
  11. import java.util.zip.CRC32;  
  12. import java.util.zip.CheckedInputStream;  
  13. import java.util.zip.CheckedOutputStream;  
  14. import java.util.zip.ZipEntry;  
  15. import java.util.zip.ZipInputStream;  
  16. import java.util.zip.ZipOutputStream;  
  17.   
  18. /** 
  19.  * ZIP壓縮工具 
  20.  *  
  21.  * @author  <a href="mailto:[email protected]">樑棟</a>    
  22.  * @since 1.0 
  23.  */  
  24. public class ZipUtils {  
  25.   
  26.     public static final String EXT = ".zip";  
  27.     private static final String BASE_DIR = "";  
  28.   
  29.     // 符號"/"用來作爲目錄標識判斷符  
  30.     private static final String PATH = "/";  
  31.     private static final int BUFFER = 1024;  
  32.   
  33.     /** 
  34.      * 壓縮 
  35.      *  
  36.      * @param srcFile 
  37.      * @throws Exception 
  38.      */  
  39.     public static void compress(File srcFile) throws Exception {  
  40.         String name = srcFile.getName();  
  41.         String basePath = srcFile.getParent();  
  42.         String destPath = basePath + name + EXT;  
  43.         compress(srcFile, destPath);  
  44.     }  
  45.   
  46.     /** 
  47.      * 壓縮 
  48.      *  
  49.      * @param srcFile 
  50.      *            源路徑 
  51.      * @param destPath 
  52.      *            目標路徑 
  53.      * @throws Exception 
  54.      */  
  55.     public static void compress(File srcFile, File destFile) throws Exception {  
  56.   
  57.         // 對輸出文件做CRC32校驗  
  58.         CheckedOutputStream cos = new CheckedOutputStream(new FileOutputStream(  
  59.                 destFile), new CRC32());  
  60.   
  61.         ZipOutputStream zos = new ZipOutputStream(cos);  
  62.   
  63.         compress(srcFile, zos, BASE_DIR);  
  64.   
  65.         zos.flush();  
  66.         zos.close();  
  67.     }  
  68.   
  69.     /** 
  70.      * 壓縮文件 
  71.      *  
  72.      * @param srcFile 
  73.      * @param destPath 
  74.      * @throws Exception 
  75.      */  
  76.     public static void compress(File srcFile, String destPath) throws Exception {  
  77.         compress(srcFile, new File(destPath));  
  78.     }  
  79.   
  80.     /** 
  81.      * 壓縮 
  82.      *  
  83.      * @param srcFile 
  84.      *            源路徑 
  85.      * @param zos 
  86.      *            ZipOutputStream 
  87.      * @param basePath 
  88.      *            壓縮包內相對路徑 
  89.      * @throws Exception 
  90.      */  
  91.     private static void compress(File srcFile, ZipOutputStream zos,  
  92.             String basePath) throws Exception {  
  93.         if (srcFile.isDirectory()) {  
  94.             compressDir(srcFile, zos, basePath);  
  95.         } else {  
  96.             compressFile(srcFile, zos, basePath);  
  97.         }  
  98.     }  
  99.   
  100.     /** 
  101.      * 壓縮 
  102.      *  
  103.      * @param srcPath 
  104.      * @throws Exception 
  105.      */  
  106.     public static void compress(String srcPath) throws Exception {  
  107.         File srcFile = new File(srcPath);  
  108.   
  109.         compress(srcFile);  
  110.     }  
  111.   
  112.     /** 
  113.      * 文件壓縮 
  114.      *  
  115.      * @param srcPath 
  116.      *            源文件路徑 
  117.      * @param destPath 
  118.      *            目標文件路徑 
  119.      *  
  120.      */  
  121.     public static void compress(String srcPath, String destPath)  
  122.             throws Exception {  
  123.         File srcFile = new File(srcPath);  
  124.   
  125.         compress(srcFile, destPath);  
  126.     }  
  127.   
  128.     /** 
  129.      * 壓縮目錄 
  130.      *  
  131.      * @param dir 
  132.      * @param zos 
  133.      * @param basePath 
  134.      * @throws Exception 
  135.      */  
  136.     private static void compressDir(File dir, ZipOutputStream zos,  
  137.             String basePath) throws Exception {  
  138.   
  139.         File[] files = dir.listFiles();  
  140.   
  141.         // 構建空目錄  
  142.         if (files.length < 1) {  
  143.             ZipEntry entry = new ZipEntry(basePath + dir.getName() + PATH);  
  144.   
  145.             zos.putNextEntry(entry);  
  146.             zos.closeEntry();  
  147.         }  
  148.   
  149.         for (File file : files) {  
  150.   
  151.             // 遞歸壓縮  
  152.             compress(file, zos, basePath + dir.getName() + PATH);  
  153.   
  154.         }  
  155.     }  
  156.   
  157.     /** 
  158.      * 文件壓縮 
  159.      *  
  160.      * @param file 
  161.      *            待壓縮文件 
  162.      * @param zos 
  163.      *            ZipOutputStream 
  164.      * @param dir 
  165.      *            壓縮文件中的當前路徑 
  166.      * @throws Exception 
  167.      */  
  168.     private static void compressFile(File file, ZipOutputStream zos, String dir)  
  169.             throws Exception {  
  170.   
  171.         /** 
  172.          * 壓縮包內文件名定義 
  173.          *  
  174.          * <pre> 
  175.          * 如果有多級目錄,那麼這裏就需要給出包含目錄的文件名 
  176.          * 如果用WinRAR打開壓縮包,中文名將顯示爲亂碼 
  177.          * </pre> 
  178.          */  
  179.         ZipEntry entry = new ZipEntry(dir + file.getName());  
  180.   
  181.         zos.putNextEntry(entry);  
  182.   
  183.         BufferedInputStream bis = new BufferedInputStream(new FileInputStream(  
  184.                 file));  
  185.   
  186.         int count;  
  187.         byte data[] = new byte[BUFFER];  
  188.         while ((count = bis.read(data, 0, BUFFER)) != -1) {  
  189.             zos.write(data, 0, count);  
  190.         }  
  191.         bis.close();  
  192.   
  193.         zos.closeEntry();  
  194.     }  
  195.   
  196. }  

來做個簡單的測試: 
Java代碼  收藏代碼
  1. import static org.junit.Assert.*;  
  2.   
  3. import org.junit.Test;  
  4.   
  5. /** 
  6.  *  
  7.  * @author 樑棟 
  8.  * @version 1.0 
  9.  * @since 1.0 
  10.  */  
  11. public class ZipUtilsTest {  
  12.   
  13.     /** 
  14.      *   
  15.      */  
  16.     @Test  
  17.     public void test() throws Exception {  
  18.         // 壓縮文件  
  19.         ZipUtils.compress("d:\\f.txt");  
  20.         // 壓縮目錄  
  21.         ZipUtils.compress("d:\\fd");  
  22.     }  
  23. }  


現在用WinRAR打開看看,是不是效果幾乎一致? 

當然,上述代碼有所不足之處主要是中文名稱亂碼問題。用java原生ZIP實現壓縮後得到的壓縮包,與系統的字符集不同,文件/目錄名將出現亂碼。這是所有歸檔壓縮都會遇到的問題。對於這種問題,Commons Copress提供瞭解決方案! 

對於解壓縮,請關注後續內容! 
發佈了5 篇原創文章 · 獲贊 0 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章