zoukankan      html  css  js  c++  java
  • Java压缩技术(二) ZIP压缩——Java原生实现

    原文:http://snowolf.iteye.com/blog/642298

    去年整理了一篇ZLib算法Java实现(Java压缩技术(一) ZLib),一直惦记却没时间补充。今天得空,整理一下ZIP的java原生实现。
    看了几篇zip压缩算法的帖子,讲的算是比较细致了,但就是没有对应的解压缩实现,太惜败了! 我就喜欢没事做总结,稍作整理,将其收纳!

    相关链接:
    Java压缩技术(一) ZLib
    Java压缩技术(二) ZIP压缩——Java原生实现
    Java压缩技术(三) ZIP解压缩——Java原生实现
    Java压缩技术(四) GZIP——Java原生实现
    Java压缩技术(五) GZIP相关——浏览器解析
    Java压缩技术(六) BZIP2——Commons实现
    Java压缩技术(七) TAR——Commons实现

    查过相关资料后才知道,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:zlex.dongliang@gmail.com">梁栋</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提供了解决方案!

    对于解压缩,请关注后续内容!

  • 相关阅读:
    Java线程同步synchronized的理解
    MySQL基础操作(二)
    MySQL基础操作(一)
    MySQL备份--xtrabackup与mysqldump工具使用
    MySQL主从复制以及在本地环境搭建
    生活在长大——第一次冲刺小任务
    现代软件工程之敏捷开发
    入门github
    我的编程人生
    Java线程
  • 原文地址:https://www.cnblogs.com/jqmtony/p/5575108.html
Copyright © 2011-2022 走看看