守望者--AIR技术交流

标题: Java压缩技术 [打印本页]

作者: 破晓    时间: 2015-1-11 13:16
标题: Java压缩技术
Java压缩技术(一) ZLib


应好友需要,整理一下Java的压缩算法,先从ZLib开始。


有关ZLib可参见官方主页 http://www.zlib.net/
ZLib可以简单的理解为压缩/解压缩算法,它与ZIP、RAR等归档算法有所不同,与bzip2比较接近。

压缩工具代码如下:

  1. /**
  2. * 2009-9-9
  3. */  
  4. package org.zlex.commons.io;  
  5.   
  6. import java.io.ByteArrayOutputStream;  
  7. import java.io.IOException;  
  8. import java.io.InputStream;  
  9. import java.io.OutputStream;  
  10. import java.util.zip.Deflater;  
  11. import java.util.zip.DeflaterOutputStream;  
  12. import java.util.zip.Inflater;  
  13. import java.util.zip.InflaterInputStream;  
  14.   
  15. /**
  16. * ZLib压缩工具
  17. *  
  18. * @author <a href="mailto:zlex.dongliang@gmail.com">梁栋</a>
  19. * @version 1.0
  20. * @since 1.0
  21. */  
  22. public abstract class ZLibUtils {  
  23.   
  24.     /**
  25.      * 压缩
  26.      *  
  27.      * @param data
  28.      *            待压缩数据
  29.      * @return byte[] 压缩后的数据
  30.      */  
  31.     public static byte[] compress(byte[] data) {  
  32.         byte[] output = new byte[0];  
  33.   
  34.         Deflater compresser = new Deflater();  
  35.   
  36.         compresser.reset();  
  37.         compresser.setInput(data);  
  38.         compresser.finish();  
  39.         ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length);  
  40.         try {  
  41.             byte[] buf = new byte[1024];  
  42.             while (!compresser.finished()) {  
  43.                 int i = compresser.deflate(buf);  
  44.                 bos.write(buf, 0, i);  
  45.             }  
  46.             output = bos.toByteArray();  
  47.         } catch (Exception e) {  
  48.             output = data;  
  49.             e.printStackTrace();  
  50.         } finally {  
  51.             try {  
  52.                 bos.close();  
  53.             } catch (IOException e) {  
  54.                 e.printStackTrace();  
  55.             }  
  56.         }  
  57.         compresser.end();  
  58.         return output;  
  59.     }  
  60.   
  61.     /**
  62.      * 压缩
  63.      *  
  64.      * @param data
  65.      *            待压缩数据
  66.      *  
  67.      * @param os
  68.      *            输出流
  69.      */  
  70.     public static void compress(byte[] data, OutputStream os) {  
  71.         DeflaterOutputStream dos = new DeflaterOutputStream(os);  
  72.   
  73.         try {  
  74.             dos.write(data, 0, data.length);  
  75.   
  76.             dos.finish();  
  77.   
  78.             dos.flush();  
  79.         } catch (IOException e) {  
  80.             e.printStackTrace();  
  81.         }  
  82.     }  
  83.   
  84.     /**
  85.      * 解压缩
  86.      *  
  87.      * @param data
  88.      *            待压缩的数据
  89.      * @return byte[] 解压缩后的数据
  90.      */  
  91.     public static byte[] decompress(byte[] data) {  
  92.         byte[] output = new byte[0];  
  93.   
  94.         Inflater decompresser = new Inflater();  
  95.         decompresser.reset();  
  96.         decompresser.setInput(data);  
  97.   
  98.         ByteArrayOutputStream o = new ByteArrayOutputStream(data.length);  
  99.         try {  
  100.             byte[] buf = new byte[1024];  
  101.             while (!decompresser.finished()) {  
  102.                 int i = decompresser.inflate(buf);  
  103.                 o.write(buf, 0, i);  
  104.             }  
  105.             output = o.toByteArray();  
  106.         } catch (Exception e) {  
  107.             output = data;  
  108.             e.printStackTrace();  
  109.         } finally {  
  110.             try {  
  111.                 o.close();  
  112.             } catch (IOException e) {  
  113.                 e.printStackTrace();  
  114.             }  
  115.         }  
  116.   
  117.         decompresser.end();  
  118.         return output;  
  119.     }  
  120.   
  121.     /**
  122.      * 解压缩
  123.      *  
  124.      * @param is
  125.      *            输入流
  126.      * @return byte[] 解压缩后的数据
  127.      */  
  128.     public static byte[] decompress(InputStream is) {  
  129.         InflaterInputStream iis = new InflaterInputStream(is);  
  130.         ByteArrayOutputStream o = new ByteArrayOutputStream(1024);  
  131.         try {  
  132.             int i = 1024;  
  133.             byte[] buf = new byte[i];  
  134.   
  135.             while ((i = iis.read(buf, 0, i)) > 0) {  
  136.                 o.write(buf, 0, i);  
  137.             }  
  138.   
  139.         } catch (IOException e) {  
  140.             e.printStackTrace();  
  141.         }  
  142.         return o.toByteArray();  
  143.     }  
  144. }  
复制代码
测试用例代码如下:
  1. /**
  2. * 2009-9-9
  3. */  
  4. package org.zlex.commons.io;  
  5.   
  6. import static org.junit.Assert.*;  
  7.   
  8. import java.io.File;  
  9. import java.io.FileInputStream;  
  10. import java.io.FileOutputStream;  
  11.   
  12. import org.junit.Test;  
  13.   
  14. /**
  15. * ZLib压缩测试用例
  16. *  
  17. * @author <a href="mailto:zlex.dongliang@gmail.com">梁栋</a>
  18. * @version 1.0
  19. * @since 1.0
  20. */  
  21. public class ZLibUtilsTest {  
  22.   
  23.     @Test  
  24.     public final void testBytes() {  
  25.         System.err.println("字节压缩/解压缩测试");  
  26.         String inputStr = "snowolf@zlex.org;dongliang@zlex.org;zlex.dongliang@zlex.org";  
  27.         System.err.println("输入字符串:\t" + inputStr);  
  28.         byte[] input = inputStr.getBytes();  
  29.         System.err.println("输入字节长度:\t" + input.length);  
  30.   
  31.         byte[] data = ZLibUtils.compress(input);  
  32.         System.err.println("压缩后字节长度:\t" + data.length);  
  33.   
  34.         byte[] output = ZLibUtils.decompress(data);  
  35.         System.err.println("解压缩后字节长度:\t" + output.length);  
  36.         String outputStr = new String(output);  
  37.         System.err.println("输出字符串:\t" + outputStr);  
  38.   
  39.         assertEquals(inputStr, outputStr);  
  40.     }  
  41.   
  42.     @Test  
  43.     public final void testFile() {  
  44.         String filename = "zlib";  
  45.         File file = new File(filename);  
  46.         System.err.println("文件压缩/解压缩测试");  
  47.         String inputStr = "snowolf@zlex.org;dongliang@zlex.org;zlex.dongliang@zlex.org";  
  48.         System.err.println("输入字符串:\t" + inputStr);  
  49.         byte[] input = inputStr.getBytes();  
  50.         System.err.println("输入字节长度:\t" + input.length);  
  51.   
  52.         try {  
  53.   
  54.             FileOutputStream fos = new FileOutputStream(file);  
  55.             ZLibUtils.compress(input, fos);  
  56.             fos.close();  
  57.             System.err.println("压缩后字节长度:\t" + file.length());  
  58.         } catch (Exception e) {  
  59.             fail(e.getMessage());  
  60.         }  
  61.   
  62.         byte[] output = null;  
  63.   
  64.         try {  
  65.             FileInputStream fis = new FileInputStream(file);  
  66.             output = ZLibUtils.decompress(fis);  
  67.             fis.close();  
  68.   
  69.         } catch (Exception e) {  
  70.             fail(e.getMessage());  
  71.         }  
  72.         System.err.println("解压缩后字节长度:\t" + output.length);  
  73.         String outputStr = new String(output);  
  74.         System.err.println("输出字符串:\t" + outputStr);  
  75.   
  76.         assertEquals(inputStr, outputStr);  
  77.     }  
  78. }  
复制代码
输入结果
  1. 字节压缩/解压缩测试  
  2. 输入字符串:  snowolf@zlex.org;dongliang@zlex.org;zlex.dongliang@zlex.org  
  3. 输入字节长度: 59  
  4. 压缩后字节长度:    39  
  5. 解压缩后字节长度:   59  
  6. 输出字符串:  snowolf@zlex.org;dongliang@zlex.org;zlex.dongliang@zlex.org  
  7. 文件压缩/解压缩测试  
  8. 输入字符串:  snowolf@zlex.org;dongliang@zlex.org;zlex.dongliang@zlex.org  
  9. 输入字节长度: 59  
  10. 压缩后字节长度:    39  
  11. 解压缩后字节长度:   59  
  12. 输出字符串:  snowolf@zlex.org;dongliang@zlex.org;zlex.dongliang@zlex.org  
复制代码
应该怎么计算呢?原数据长度59字节,压缩后39字节,大约是33%的压缩率!

ZLib压缩对大字节数据压缩,才能反映出压缩效果。
先占个位儿,回头细致整理!



本系列文章来自:http://snowolf.iteye.com/blog/465433

作者: 破晓    时间: 2015-1-11 13:19
标题: Java压缩技术(二) ZIP压缩——Java原生实现
Java压缩技术(二) ZIP压缩——Java原生实现


去年整理了一篇ZLib算法Java实现(
Java压缩技术(一) ZLib),一直惦记却没时间补充。今天得空,整理一下ZIP的java原生实现。
看了几篇zip压缩算法的帖子,讲的算是比较细致了,但就是没有对应的解压缩实现,太惜败了! 我就喜欢没事做总结,稍作整理,将其收纳!
查过相关资料后才知道,ZIP应该算作归档类的压缩算法,每一门学科都可深可浅!

闲言少叙,先说ZIP压缩。
zip压缩需要通过ZipOutputStream 执行write方法将压缩数据写到指定输出流中。
注意,这里应先使用CheckedOutputStream 指定文件校验算法。(通常使用CRC32算法)。代码如下所示:
  1. CheckedOutputStream cos = new CheckedOutputStream(new FileOutputStream(destPath), new CRC32());  
  2. ZipOutputStream zos = new ZipOutputStream(cos);  
复制代码
接下来,需要将待压缩文件以ZipEntry的方式追加到压缩文件中,如下所示:
  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方法,如下所示:
  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="/";)作为添加项名字结尾符!

递归构建目录压缩,代码如下:
  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打开后,可以看到这个目录下还有一个空文件名文件!


来个完整的压缩实现,代码如下所示:
  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. }  
复制代码
来做个简单的测试:
  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提供了解决方案!

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

作者: 破晓    时间: 2015-1-11 13:24
Java压缩技术(三) ZIP解压缩——Java原生实现


JavaEye的朋友跟我说:“你一口气把ZIP压缩和解压缩都写到一个帖子里,我看起来很累,不如分开好阅读”。ok,面向读者需求,我做调整,这里单说ZIP解压缩!

解压缩与压缩运作方式相反,原理大抵相同,由ZipInputStream通过read方法对数据解压,同时需要通过CheckedInputStream设置冗余校验码,如:
  1. CheckedInputStream cis = new CheckedInputStream(new FileInputStream(  
  2.         srcFile), new CRC32());  
  3.   
  4. ZipInputStream zis = new ZipInputStream(cis);  
复制代码
需要注意的是,在构建解压文件时,需要考虑目录的自动创建,这里通过递归方式逐层创建父目录,如下所示:
  1. /**
  2. * 文件探针
  3. *  
  4. *  
  5. * 当父目录不存在时,创建目录!
  6. *  
  7. *  
  8. * @param dirFile
  9. */  
  10. private static void fileProber(File dirFile) {  
  11.   
  12.     File parentFile = dirFile.getParentFile();  
  13.     if (!parentFile.exists()) {  
  14.   
  15.         // 递归寻找上级目录  
  16.         fileProber(parentFile);  
  17.   
  18.         parentFile.mkdir();  
  19.     }  
  20.   
  21. }  
复制代码
在压缩的时候,我们是将一个一个文件作为压缩添加项(ZipEntry)添加至压缩包中,解压缩就要将一个一个压缩项从压缩包中提取出来,如下所示:

  1. /**
  2. * 文件 解压缩
  3. *  
  4. * @param destFile
  5. *            目标文件
  6. * @param zis
  7. *            ZipInputStream
  8. * @throws Exception
  9. */  
  10. private static void decompress(File destFile, ZipInputStream zis)  
  11.         throws Exception {  
  12.   
  13.     ZipEntry entry = null;  
  14.     while ((entry = zis.getNextEntry()) != null) {  
  15.   
  16.         // 文件  
  17.         String dir = destFile.getPath() + File.separator + entry.getName();  
  18.   
  19.         File dirFile = new File(dir);  
  20.   
  21.         // 文件检查  
  22.         fileProber(dirFile);  
  23.   
  24.             if (entry.isDirectory()){  
  25.                 dirFile.mkdirs();  
  26.             } else {  
  27.             decompressFile(dirFile, zis);  
  28.             }  
  29.   
  30.             zis.closeEntry();  
  31.     }  
  32. }  
复制代码
最核心的解压缩实现,其实与压缩实现非常相似,代码如下所示:
  1. /**
  2. * 文件解压缩
  3. *  
  4. * @param destFile
  5. *            目标文件
  6. * @param zis
  7. *            ZipInputStream
  8. * @throws Exception
  9. */  
  10. private static void decompressFile(File destFile, ZipInputStream zis)  
  11.         throws Exception {  
  12.   
  13.     BufferedOutputStream bos = new BufferedOutputStream(  
  14.             new FileOutputStream(destFile));  
  15.   
  16.     int count;  
  17.     byte data[] = new byte[BUFFER];  
  18.     while ((count = zis.read(data, 0, BUFFER)) != -1) {  
  19.         bos.write(data, 0, count);  
  20.     }  
  21.   
  22.     bos.close();  
  23. }  
复制代码
来个完整的解压缩实现,代码如下:
  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 梁栋   
  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.     private static final String PATH = File.separator;  
  29.     private static final int BUFFER = 1024;  
  30.   
  31.     /**
  32.      * 文件 解压缩
  33.      *  
  34.      * @param srcPath
  35.      *            源文件路径
  36.      *  
  37.      * @throws Exception
  38.      */  
  39.     public static void decompress(String srcPath) throws Exception {  
  40.         File srcFile = new File(srcPath);  
  41.   
  42.         decompress(srcFile);  
  43.     }  
  44.   
  45.     /**
  46.      * 解压缩
  47.      *  
  48.      * @param srcFile
  49.      * @throws Exception
  50.      */  
  51.     public static void decompress(File srcFile) throws Exception {  
  52.         String basePath = srcFile.getParent();  
  53.         decompress(srcFile, basePath);  
  54.     }  
  55.   
  56.     /**
  57.      * 解压缩
  58.      *  
  59.      * @param srcFile
  60.      * @param destFile
  61.      * @throws Exception
  62.      */  
  63.     public static void decompress(File srcFile, File destFile) throws Exception {  
  64.   
  65.         CheckedInputStream cis = new CheckedInputStream(new FileInputStream(  
  66.                 srcFile), new CRC32());  
  67.   
  68.         ZipInputStream zis = new ZipInputStream(cis);  
  69.   
  70.         decompress(destFile, zis);  
  71.   
  72.         zis.close();  
  73.   
  74.     }  
  75.   
  76.     /**
  77.      * 解压缩
  78.      *  
  79.      * @param srcFile
  80.      * @param destPath
  81.      * @throws Exception
  82.      */  
  83.     public static void decompress(File srcFile, String destPath)  
  84.             throws Exception {  
  85.         decompress(srcFile, new File(destPath));  
  86.   
  87.     }  
  88.   
  89.     /**
  90.      * 文件 解压缩
  91.      *  
  92.      * @param srcPath
  93.      *            源文件路径
  94.      * @param destPath
  95.      *            目标文件路径
  96.      * @throws Exception
  97.      */  
  98.     public static void decompress(String srcPath, String destPath)  
  99.             throws Exception {  
  100.   
  101.         File srcFile = new File(srcPath);  
  102.         decompress(srcFile, destPath);  
  103.     }  
  104.   
  105.     /**
  106.      * 文件 解压缩
  107.      *  
  108.      * @param destFile
  109.      *            目标文件
  110.      * @param zis
  111.      *            ZipInputStream
  112.      * @throws Exception
  113.      */  
  114.     private static void decompress(File destFile, ZipInputStream zis)  
  115.             throws Exception {  
  116.   
  117.         ZipEntry entry = null;  
  118.         while ((entry = zis.getNextEntry()) != null) {  
  119.   
  120.             // 文件  
  121.             String dir = destFile.getPath() + File.separator + entry.getName();  
  122.   
  123.             File dirFile = new File(dir);  
  124.   
  125.             // 文件检查  
  126.             fileProber(dirFile);  
  127.   
  128.             if (entry.isDirectory()) {  
  129.                 dirFile.mkdirs();  
  130.             } else {  
  131.                 decompressFile(dirFile, zis);  
  132.             }  
  133.   
  134.             zis.closeEntry();  
  135.         }  
  136.     }  
  137.   
  138.     /**
  139.      * 文件探针
  140.      *  
  141.      *  
  142.      * 当父目录不存在时,创建目录!
  143.      *  
  144.      *  
  145.      * @param dirFile
  146.      */  
  147.     private static void fileProber(File dirFile) {  
  148.   
  149.         File parentFile = dirFile.getParentFile();  
  150.         if (!parentFile.exists()) {  
  151.   
  152.             // 递归寻找上级目录  
  153.             fileProber(parentFile);  
  154.   
  155.             parentFile.mkdir();  
  156.         }  
  157.   
  158.     }  
  159.   
  160.     /**
  161.      * 文件解压缩
  162.      *  
  163.      * @param destFile
  164.      *            目标文件
  165.      * @param zis
  166.      *            ZipInputStream
  167.      * @throws Exception
  168.      */  
  169.     private static void decompressFile(File destFile, ZipInputStream zis)  
  170.             throws Exception {  
  171.   
  172.         BufferedOutputStream bos = new BufferedOutputStream(  
  173.                 new FileOutputStream(destFile));  
  174.   
  175.         int count;  
  176.         byte data[] = new byte[BUFFER];  
  177.         while ((count = zis.read(data, 0, BUFFER)) != -1) {  
  178.             bos.write(data, 0, count);  
  179.         }  
  180.   
  181.         bos.close();  
  182.     }  
  183.   
  184. }  
复制代码
其实,理解了ZIP的工作原理,这些代码看起来很好懂!

把刚才做的压缩文件再用上述代码解开看看,测试用例如下:
  1. /**
  2. * 2010-4-12
  3. */  
  4. package org.zlex.commons.io;  
  5.   
  6. import static org.junit.Assert.*;  
  7.   
  8. import org.junit.Test;  
  9.   
  10. /**
  11. *  
  12. * @author 梁栋
  13. * @version 1.0
  14. * @since 1.0
  15. */  
  16. public class ZipUtilsTest {  
  17.   
  18.     /**
  19.      *   
  20.      */  
  21.     @Test  
  22.     public void test() throws Exception {  
  23.         // 解压到指定目录  
  24.         ZipUtils.decompress("d:\\f.txt.zip", "d:\\ff");  
  25.         // 解压到当前目录  
  26.         ZipUtils.decompress("d:\\fd.zip");  
  27.     }  
  28.   
  29. }  
复制代码
完整代码详见附件!

java原生的ZIP实现虽然在压缩时会因与系统字符集不符产生中文乱码,但在解压缩后,字符集即可恢复。

除了java原生的ZIP实现外,commons和ant也提供了相应的ZIP算法实现,有机会我再一一介绍!
[attach]353[/attach]

作者: 破晓    时间: 2015-1-11 13:30
标题: Java压缩技术(四) GZIP——Java原生实现
Java压缩技术(四) GZIP——Java原生实现

趁着头脑清楚,抓紧时间继续整理!
熟悉linux的朋友可能都用过文件压缩命令,譬如最为简单的gzip命令。


GZIP常常用在linxu环境下,是一种非常简单的压缩算法。在Java实现API中,它仅仅包含两个实现类:GZIPInputStream和GZIPOutputStream。
GZIPOutputStream类用于压缩
GZIPInputStream类用于解压缩

先说压缩实现,GZIPOutputStream只有一个方法用于压缩,就是带定长的write方法。简单调用如下文所示:
  1. /**
  2. * 数据压缩
  3. *  
  4. * @param is
  5. * @param os
  6. * @throws Exception
  7. */  
  8. public static void compress(InputStream is, OutputStream os)  
  9.         throws Exception {  
  10.   
  11.     GZIPOutputStream gos = new GZIPOutputStream(os);  
  12.   
  13.     int count;  
  14.     byte data[] = new byte[BUFFER];  
  15.     while ((count = is.read(data, 0, BUFFER)) != -1) {  
  16.         gos.write(data, 0, count);  
  17.     }  
  18.   
  19.     gos.finish();  
  20.   
  21.     gos.flush();  
  22.     gos.close();  
  23. }  
复制代码
记得完成操作后,调用finish方法和flush方法!

核心的压缩实现就这么多!

对于解压缩,GZIPInputStream也对应GZIPOutputStream提供了一个带定长的read方法。简单调用如下文所示:
  1. /**
  2. * 数据解压缩
  3. *  
  4. * @param is
  5. * @param os
  6. * @throws Exception
  7. */  
  8. public static void decompress(InputStream is, OutputStream os)  
  9.         throws Exception {  
  10.   
  11.     GZIPInputStream gis = new GZIPInputStream(is);  
  12.   
  13.     int count;  
  14.     byte data[] = new byte[BUFFER];  
  15.     while ((count = gis.read(data, 0, BUFFER)) != -1) {  
  16.         os.write(data, 0, count);  
  17.     }  
  18.   
  19.     gis.close();  
  20. }  
复制代码
就这么简单! 核心内容完毕!

顺便补充一下,在liunx下操作gzip命令

gzip file用于压缩,如gzip a.txt将得到文件a.txt.gz同时删除文件a.txt!
gzip -d file.gz用于解压缩,如gzip -d a.txt.gz将得到文件a.txt同时删除文件a.txt.gz!

根据这些特性,我补充了相应的文件操作实现,详见下文!

完整实现:
  1. /**
  2. * 2010-4-13
  3. */  
  4. package org.zlex.commons.io;  
  5.   
  6. import java.io.ByteArrayInputStream;  
  7. import java.io.ByteArrayOutputStream;  
  8. import java.io.File;  
  9. import java.io.FileInputStream;  
  10. import java.io.FileOutputStream;  
  11. import java.io.InputStream;  
  12. import java.io.OutputStream;  
  13. import java.util.zip.GZIPInputStream;  
  14. import java.util.zip.GZIPOutputStream;  
  15.   
  16. /**
  17. * GZIP工具
  18. *  
  19. * @author <a href="mailto:zlex.dongliang@gmail.com">梁栋</a>
  20. * @since 1.0
  21. */  
  22. public abstract class GZipUtils {  
  23.   
  24.     public static final int BUFFER = 1024;  
  25.     public static final String EXT = ".gz";  
  26.   
  27.     /**
  28.      * 数据压缩
  29.      *  
  30.      * @param data
  31.      * @return
  32.      * @throws Exception
  33.      */  
  34.     public static byte[] compress(byte[] data) throws Exception {  
  35.         ByteArrayInputStream bais = new ByteArrayInputStream(data);  
  36.         ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  37.   
  38.         // 压缩  
  39.         compress(bais, baos);  
  40.   
  41.         byte[] output = baos.toByteArray();  
  42.   
  43.         baos.flush();  
  44.         baos.close();  
  45.   
  46.         bais.close();  
  47.   
  48.         return output;  
  49.     }  
  50.   
  51.     /**
  52.      * 文件压缩
  53.      *  
  54.      * @param file
  55.      * @throws Exception
  56.      */  
  57.     public static void compress(File file) throws Exception {  
  58.         compress(file, true);  
  59.     }  
  60.   
  61.     /**
  62.      * 文件压缩
  63.      *  
  64.      * @param file
  65.      * @param delete
  66.      *            是否删除原始文件
  67.      * @throws Exception
  68.      */  
  69.     public static void compress(File file, boolean delete) throws Exception {  
  70.         FileInputStream fis = new FileInputStream(file);  
  71.         FileOutputStream fos = new FileOutputStream(file.getPath() + EXT);  
  72.   
  73.         compress(fis, fos);  
  74.   
  75.         fis.close();  
  76.         fos.flush();  
  77.         fos.close();  
  78.   
  79.         if (delete) {  
  80.             file.delete();  
  81.         }  
  82.     }  
  83.   
  84.     /**
  85.      * 数据压缩
  86.      *  
  87.      * @param is
  88.      * @param os
  89.      * @throws Exception
  90.      */  
  91.     public static void compress(InputStream is, OutputStream os)  
  92.             throws Exception {  
  93.   
  94.         GZIPOutputStream gos = new GZIPOutputStream(os);  
  95.   
  96.         int count;  
  97.         byte data[] = new byte[BUFFER];  
  98.         while ((count = is.read(data, 0, BUFFER)) != -1) {  
  99.             gos.write(data, 0, count);  
  100.         }  
  101.   
  102.         gos.finish();  
  103.   
  104.         gos.flush();  
  105.         gos.close();  
  106.     }  
  107.   
  108.     /**
  109.      * 文件压缩
  110.      *  
  111.      * @param path
  112.      * @throws Exception
  113.      */  
  114.     public static void compress(String path) throws Exception {  
  115.         compress(path, true);  
  116.     }  
  117.   
  118.     /**
  119.      * 文件压缩
  120.      *  
  121.      * @param path
  122.      * @param delete
  123.      *            是否删除原始文件
  124.      * @throws Exception
  125.      */  
  126.     public static void compress(String path, boolean delete) throws Exception {  
  127.         File file = new File(path);  
  128.         compress(file, delete);  
  129.     }  
  130.   
  131.     /**
  132.      * 数据解压缩
  133.      *  
  134.      * @param data
  135.      * @return
  136.      * @throws Exception
  137.      */  
  138.     public static byte[] decompress(byte[] data) throws Exception {  
  139.         ByteArrayInputStream bais = new ByteArrayInputStream(data);  
  140.         ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  141.   
  142.         // 解压缩  
  143.   
  144.         decompress(bais, baos);  
  145.   
  146.         data = baos.toByteArray();  
  147.   
  148.         baos.flush();  
  149.         baos.close();  
  150.   
  151.         bais.close();  
  152.   
  153.         return data;  
  154.     }  
  155.   
  156.     /**
  157.      * 文件解压缩
  158.      *  
  159.      * @param file
  160.      * @throws Exception
  161.      */  
  162.     public static void decompress(File file) throws Exception {  
  163.         decompress(file, true);  
  164.     }  
  165.   
  166.     /**
  167.      * 文件解压缩
  168.      *  
  169.      * @param file
  170.      * @param delete
  171.      *            是否删除原始文件
  172.      * @throws Exception
  173.      */  
  174.     public static void decompress(File file, boolean delete) throws Exception {  
  175.         FileInputStream fis = new FileInputStream(file);  
  176.         FileOutputStream fos = new FileOutputStream(file.getPath().replace(EXT,  
  177.                 ""));  
  178.         decompress(fis, fos);  
  179.         fis.close();  
  180.         fos.flush();  
  181.         fos.close();  
  182.   
  183.         if (delete) {  
  184.             file.delete();  
  185.         }  
  186.     }  
  187.   
  188.     /**
  189.      * 数据解压缩
  190.      *  
  191.      * @param is
  192.      * @param os
  193.      * @throws Exception
  194.      */  
  195.     public static void decompress(InputStream is, OutputStream os)  
  196.             throws Exception {  
  197.   
  198.         GZIPInputStream gis = new GZIPInputStream(is);  
  199.   
  200.         int count;  
  201.         byte data[] = new byte[BUFFER];  
  202.         while ((count = gis.read(data, 0, BUFFER)) != -1) {  
  203.             os.write(data, 0, count);  
  204.         }  
  205.   
  206.         gis.close();  
  207.     }  
  208.   
  209.     /**
  210.      * 文件解压缩
  211.      *  
  212.      * @param path
  213.      * @throws Exception
  214.      */  
  215.     public static void decompress(String path) throws Exception {  
  216.         decompress(path, true);  
  217.     }  
  218.   
  219.     /**
  220.      * 文件解压缩
  221.      *  
  222.      * @param path
  223.      * @param delete
  224.      *            是否删除原始文件
  225.      * @throws Exception
  226.      */  
  227.     public static void decompress(String path, boolean delete) throws Exception {  
  228.         File file = new File(path);  
  229.         decompress(file, delete);  
  230.     }  
  231.   
  232. }  
复制代码
罗嗦了半天,到底行不行?
来个测试用例,测试用例如下所示:
  1. /**
  2. * 2010-4-13
  3. */  
  4. package org.zlex.commons.compress.compress;  
  5.   
  6. import static org.junit.Assert.assertEquals;  
  7.   
  8. import java.io.DataInputStream;  
  9. import java.io.File;  
  10. import java.io.FileInputStream;  
  11. import java.io.FileOutputStream;  
  12.   
  13. import org.junit.Test;  
  14.   
  15. /**
  16. * @author <a href="mailto:zlex.dongliang@gmail.com">梁栋</a>
  17. * @since 1.0
  18. */  
  19. public class GZipUtilsTest {  
  20.   
  21.     private String inputStr = "zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org";  
  22.   
  23.     @Test  
  24.     public final void testDataCompress() throws Exception {  
  25.   
  26.         System.err.println("原文:\t" + inputStr);  
  27.   
  28.         byte[] input = inputStr.getBytes();  
  29.         System.err.println("长度:\t" + input.length);  
  30.   
  31.         byte[] data = GZipUtils.compress(input);  
  32.         System.err.println("压缩后:\t");  
  33.         System.err.println("长度:\t" + data.length);  
  34.   
  35.         byte[] output = GZipUtils.decompress(data);  
  36.         String outputStr = new String(output);  
  37.         System.err.println("解压缩后:\t" + outputStr);  
  38.         System.err.println("长度:\t" + output.length);  
  39.   
  40.         assertEquals(inputStr, outputStr);  
  41.   
  42.     }  
  43.   
  44.     @Test  
  45.     public final void testFileCompress() throws Exception {  
  46.   
  47.         FileOutputStream fos = new FileOutputStream("d:/f.txt");  
  48.   
  49.         fos.write(inputStr.getBytes());  
  50.         fos.flush();  
  51.         fos.close();  
  52.   
  53.         GZipUtils.compress("d:/f.txt", false);  
  54.   
  55.         GZipUtils.decompress("d:/f.txt.gz", false);  
  56.   
  57.         File file = new File("d:/f.txt");  
  58.   
  59.         FileInputStream fis = new FileInputStream(file);  
  60.   
  61.         DataInputStream dis = new DataInputStream(fis);  
  62.   
  63.         byte[] data = new byte[(int) file.length()];  
  64.         dis.readFully(data);  
  65.   
  66.         fis.close();  
  67.   
  68.         String outputStr = new String(data);  
  69.         assertEquals(inputStr, outputStr);  
  70.     }  
  71. }  
复制代码
结果如何?
先看testDataCompress()方法控制台输出结果。
控制台输出如下:
  1. 原文:        zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org
  2. 长度:        52
  3. 压缩后:        
  4. 长度:        45
  5. 解压缩后:        zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org
  6. 长度:        52
复制代码
这里使用英文字符做测试,当输入字符串的字节数大于50左右时,压缩效果明显;如果这里使用中文压缩,可能当压缩上千字节时方能体现出压缩效果!
对于文件操作,朋友们可以自行实验,我代码里的实现是按照gzip命令来的!
举例来说:
压缩时,将文件a.txt压缩为a.txt.gz,同时删除文件a.txt。
解压缩时,将文件a.txt.gz解压缩为a.txt,同时删除文件a.txt.gz。

注意执行testFileCompress方法,查看产生的文件! 你大可以放到linux上去做验证!

commons也提供了GZIP算法的实现,甚至更多种压缩算法(tar、bzip2等)的实现,有机会我将继续整理!


[attach]354[/attach]



作者: 破晓    时间: 2015-1-11 13:35
标题: Java压缩技术(五) GZIP相关——浏览器解析
Java压缩技术(五) GZIP相关——浏览器解析

昨天一篇GZIP相关实现引起大家的广泛关注,为一位朋友验证一下浏览器对gzip的支持情况,才有此文!


GZIP本身就是一种网络流压缩算法,而且应用相当广泛。如果网络访问过程中,其数据流较大,势必降低网络访问效率,此时就需要考虑使用压缩!当然,在浏览器与服务器的交互中,要考虑浏览器是否支持这些算法,以及服务器运行压缩所带来的负载。如果你关注浏览器上传的“Accept-Encoding”属性,你就能看明白这一点。

GZIP如何压缩,我这里就不废话了,不清楚的朋友请关注(Java压缩技术(四) GZIP——Java原生实现
至于如何使得服务器支持gzip的流输出,关键点只有一行代码:
  1. response.setHeader("Content-Encoding", "gzip");  
复制代码
经过这项设置,浏览器就能够很好的作出响应!
先看FireFox,如图:


[attach]355[/attach]

再看Opera,如图:


[attach]356[/attach]

浏览器在打开该页面时,就可解压获得并展示其内容!
至于IE就惨了,愣是要求下载! 可能缺少什么设置!(注意后续服务器配置! 不是无解哦!)
你不信就这么简单?上代码!
  1. /**
  2. * 2010-4-14
  3. */  
  4. import java.io.ByteArrayOutputStream;  
  5. import java.io.IOException;  
  6. import java.io.OutputStream;  
  7. import java.util.zip.GZIPOutputStream;  
  8.   
  9. import javax.servlet.ServletException;  
  10. import javax.servlet.http.HttpServlet;  
  11. import javax.servlet.http.HttpServletRequest;  
  12. import javax.servlet.http.HttpServletResponse;  
  13.   
  14. /**
  15. * GZip服务
  16. *  
  17. * @author <a href="mailto:zlex.dongliang@gmail.com">梁栋</a>
  18. * @since 1.0
  19. */  
  20. public class GZipServlet extends HttpServlet {  
  21.   
  22.     private static final long serialVersionUID = -4811926975427533081L;  
  23.     private static final String ENCODING = "UTF-8";  
  24.   
  25.     /**
  26.      * 压缩
  27.      *  
  28.      * @param data
  29.      * @return
  30.      * @throws Exception
  31.      */  
  32.     private byte[] compress(byte[] data) throws Exception {  
  33.   
  34.         ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  35.   
  36.         // 压缩  
  37.         GZIPOutputStream gos = new GZIPOutputStream(baos);  
  38.   
  39.         gos.write(data, 0, data.length);  
  40.   
  41.         gos.finish();  
  42.   
  43.         byte[] output = baos.toByteArray();  
  44.   
  45.         baos.flush();  
  46.         baos.close();  
  47.   
  48.         return output;  
  49.     }  
  50.   
  51.     /**
  52.      * @param request
  53.      * @param response
  54.      * @throws ServletException
  55.      * @throws IOException
  56.      */  
  57.     private void excute(HttpServletRequest request, HttpServletResponse response)  
  58.             throws ServletException, IOException {  
  59.   
  60.         byte[] data = "我是一个中国人!".getBytes(ENCODING);  
  61.   
  62.         try {  
  63.             byte[] output = compress(data);  
  64.   
  65.             // 设置Content-Encoding,这是关键点!  
  66.             response.setHeader("Content-Encoding", "gzip");  
  67.             // 设置字符集  
  68.             response.setCharacterEncoding(ENCODING);  
  69.             // 设定输出流中内容长度  
  70.             response.setContentLength(output.length);  
  71.   
  72.             OutputStream out = response.getOutputStream();  
  73.             out.write(output);  
  74.             out.flush();  
  75.             out.close();  
  76.         } catch (Exception e) {  
  77.             e.printStackTrace();  
  78.         }  
  79.     }  
  80.   
  81.     /**
  82.      * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
  83.      *      response)
  84.      */  
  85.     protected void doGet(HttpServletRequest request,  
  86.             HttpServletResponse response) throws ServletException, IOException {  
  87.         excute(request, response);  
  88.     }  
  89.   
  90.     /**
  91.      * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
  92.      *      response)
  93.      */  
  94.     protected void doPost(HttpServletRequest request,  
  95.             HttpServletResponse response) throws ServletException, IOException {  
  96.         excute(request, response);  
  97.     }  
  98.   
  99. }  
复制代码
其实,就这些内容!

当然,除了上述代码方式,针对于tomcat可以通过修改配置获得支持!
  1. <Connector   
  2.               port="8080"   
  3.               protocol="HTTP/1.1"  
  4.               connectionTimeout="20000"  
  5.               redirectPort="443"   
  6.               URIEncoding="UTF-8"  
  7.               compression="on"     
  8.               noCompressionUserAgents="gozilla, traviata"     
  9.               compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain,application/json"   
  10. />   
复制代码
说说配置细节:
compression="on"   开启压缩支持
noCompressionUserAgents="gozilla, traviata"   不压缩的内容
compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain,application/json" 压缩的类型

至于代码,你无需写一行代码!轻松获得服务器访问加速! 并且IE也支持哦!

借网友经验,如下实现,轻松获得IE支持:
  1. response.setContentType("text/plain;charset=utf-8");   
复制代码
工程在我的附件里,请下载验证!
[attach]357[/attach]



作者: 破晓    时间: 2015-1-11 13:39
标题: Java压缩技术(六) BZIP2——Commons实现
Java压缩技术(六) BZIP2——Commons实现



想要把一整套算法都整理出来,的确是件非常老心费力的事情! 但是如果一件事情不能有始有终,难免会有遗憾! 索性,继续整理!



BZip2与GZip有什么渊源,我这里不深究。我要说的只是,这两种算法,你在linux下都可以找到相应的操作命令。

GZip
压缩
gzip <file> 将得到压缩文件<file>.gz,同时删除文件<file>
解压缩
gzip -d <file>.gz 将得到压缩文件<file>,同时删除文件<file>.gz

BZip2与之相当,几乎没有什么差别~~
BZip2
压缩
bzip2 <file> 将得到压缩文件<file>.bz2,同时删除文件<file>
解压缩
bzip2 -d <file>.bz2 将得到压缩文件<file>,同时删除文件<file>.bz2

除了命令不同外,几乎是一样的!

再说实现。GZIP是JDK自带的算法实现,但BZip2则不曾享受这个待遇。 不过,强大的Apache坚决不会让这些个在Linux下如鱼得水的算法在Java世界中销声匿迹。Apache在Commons Compress中提供了相应的实现。同时,还包括众所周知的tar、cpio、zip等算法实现,其中最为丰富的当属zip实现了!

我继续依葫芦画瓢~~~
BZip2CompressorOutputStream类用于压缩
BZip2CompressorInputStream类用于解压缩

先说压缩实现,BZip2CompressorOutputStream只有一个方法用于压缩,就是带定长的write方法。简单调用如下文所示:
  1. /**
  2. * 数据压缩
  3. *  
  4. * @param is
  5. * @param os
  6. * @throws Exception
  7. */  
  8. public static void compress(InputStream is, OutputStream os)  
  9.         throws Exception {  
  10.   
  11.     BZip2CompressorOutputStream gos = new BZip2CompressorOutputStream(os);  
  12.   
  13.     int count;  
  14.     byte data[] = new byte[BUFFER];  
  15.     while ((count = is.read(data, 0, BUFFER)) != -1) {  
  16.         gos.write(data, 0, count);  
  17.     }  
  18.   
  19.     gos.finish();  
  20.   
  21.     gos.flush();  
  22.     gos.close();  
  23. }  
复制代码
与GZip实现有何差别?除了换掉了GZIPOutputStream没有任何差别。

解压缩就更不用说了,BZip2CompressorInputStream提供了一个带定长的read方法。简单调用如下文所示:
  1. /**
  2. * 数据解压缩
  3. *  
  4. * @param is
  5. * @param os
  6. * @throws Exception
  7. */  
  8. public static void decompress(InputStream is, OutputStream os)  
  9.         throws Exception {  
  10.   
  11.     BZip2CompressorInputStream gis = new BZip2CompressorInputStream(is);  
  12.   
  13.     int count;  
  14.     byte data[] = new byte[BUFFER];  
  15.     while ((count = gis.read(data, 0, BUFFER)) != -1) {  
  16.         os.write(data, 0, count);  
  17.     }  
  18.   
  19.     gis.close();  
  20. }  
复制代码
嗯,没什么难度!
IT这行就是这样,只要你肯用心,能触类旁通,就能融会贯通!
给一个完整实现:
  1. /**
  2. * 2010-4-15
  3. */  
  4. package org.zlex.commons.compress.compress;  
  5.   
  6. import java.io.ByteArrayInputStream;  
  7. import java.io.ByteArrayOutputStream;  
  8. import java.io.File;  
  9. import java.io.FileInputStream;  
  10. import java.io.FileOutputStream;  
  11. import java.io.InputStream;  
  12. import java.io.OutputStream;  
  13.   
  14. import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;  
  15. import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream;  
  16.   
  17. /**
  18. * BZip2工具
  19. *  
  20. * @author  <a href="mailto:zlex.dongliang@gmail.com">梁栋</a>
  21. * @since 1.0
  22. */  
  23. public abstract class BZip2Utils {  
  24.   
  25.     public static final int BUFFER = 1024;  
  26.     public static final CharSequence EXT = ".bz2";  
  27.   
  28.     /**
  29.      * 数据压缩
  30.      *  
  31.      * @param data
  32.      * @return
  33.      * @throws Exception
  34.      */  
  35.     public static byte[] compress(byte[] data) throws Exception {  
  36.         ByteArrayInputStream bais = new ByteArrayInputStream(data);  
  37.         ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  38.   
  39.         // 压缩  
  40.         compress(bais, baos);  
  41.   
  42.         byte[] output = baos.toByteArray();  
  43.   
  44.         baos.flush();  
  45.         baos.close();  
  46.   
  47.         bais.close();  
  48.   
  49.         return output;  
  50.     }  
  51.   
  52.     /**
  53.      * 文件压缩
  54.      *  
  55.      * @param file
  56.      * @throws Exception
  57.      */  
  58.     public static void compress(File file) throws Exception {  
  59.         compress(file, true);  
  60.     }  
  61.   
  62.     /**
  63.      * 文件压缩
  64.      *  
  65.      * @param file
  66.      * @param delete
  67.      *            是否删除原始文件
  68.      * @throws Exception
  69.      */  
  70.     public static void compress(File file, boolean delete) throws Exception {  
  71.         FileInputStream fis = new FileInputStream(file);  
  72.         FileOutputStream fos = new FileOutputStream(file.getPath() + EXT);  
  73.   
  74.         compress(fis, fos);  
  75.   
  76.         fis.close();  
  77.         fos.flush();  
  78.         fos.close();  
  79.   
  80.         if (delete) {  
  81.             file.delete();  
  82.         }  
  83.     }  
  84.   
  85.     /**
  86.      * 数据压缩
  87.      *  
  88.      * @param is
  89.      * @param os
  90.      * @throws Exception
  91.      */  
  92.     public static void compress(InputStream is, OutputStream os)  
  93.             throws Exception {  
  94.   
  95.         BZip2CompressorOutputStream gos = new BZip2CompressorOutputStream(os);  
  96.   
  97.         int count;  
  98.         byte data[] = new byte[BUFFER];  
  99.         while ((count = is.read(data, 0, BUFFER)) != -1) {  
  100.             gos.write(data, 0, count);  
  101.         }  
  102.   
  103.         gos.finish();  
  104.   
  105.         gos.flush();  
  106.         gos.close();  
  107.     }  
  108.   
  109.     /**
  110.      * 文件压缩
  111.      *  
  112.      * @param path
  113.      * @throws Exception
  114.      */  
  115.     public static void compress(String path) throws Exception {  
  116.         compress(path, true);  
  117.     }  
  118.   
  119.     /**
  120.      * 文件压缩
  121.      *  
  122.      * @param path
  123.      * @param delete
  124.      *            是否删除原始文件
  125.      * @throws Exception
  126.      */  
  127.     public static void compress(String path, boolean delete) throws Exception {  
  128.         File file = new File(path);  
  129.         compress(file, delete);  
  130.     }  
  131.   
  132.     /**
  133.      * 数据解压缩
  134.      *  
  135.      * @param data
  136.      * @return
  137.      * @throws Exception
  138.      */  
  139.     public static byte[] decompress(byte[] data) throws Exception {  
  140.         ByteArrayInputStream bais = new ByteArrayInputStream(data);  
  141.         ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  142.   
  143.         // 解压缩  
  144.   
  145.         decompress(bais, baos);  
  146.   
  147.         data = baos.toByteArray();  
  148.   
  149.         baos.flush();  
  150.         baos.close();  
  151.   
  152.         bais.close();  
  153.   
  154.         return data;  
  155.     }  
  156.   
  157.     /**
  158.      * 文件解压缩
  159.      *  
  160.      * @param file
  161.      * @throws Exception
  162.      */  
  163.     public static void decompress(File file) throws Exception {  
  164.         decompress(file, true);  
  165.     }  
  166.   
  167.     /**
  168.      * 文件解压缩
  169.      *  
  170.      * @param file
  171.      * @param delete
  172.      *            是否删除原始文件
  173.      * @throws Exception
  174.      */  
  175.     public static void decompress(File file, boolean delete) throws Exception {  
  176.         FileInputStream fis = new FileInputStream(file);  
  177.         FileOutputStream fos = new FileOutputStream(file.getPath().replace(EXT,  
  178.                 ""));  
  179.         decompress(fis, fos);  
  180.         fis.close();  
  181.         fos.flush();  
  182.         fos.close();  
  183.   
  184.         if (delete) {  
  185.             file.delete();  
  186.         }  
  187.     }  
  188.   
  189.     /**
  190.      * 数据解压缩
  191.      *  
  192.      * @param is
  193.      * @param os
  194.      * @throws Exception
  195.      */  
  196.     public static void decompress(InputStream is, OutputStream os)  
  197.             throws Exception {  
  198.   
  199.         BZip2CompressorInputStream gis = new BZip2CompressorInputStream(is);  
  200.   
  201.         int count;  
  202.         byte data[] = new byte[BUFFER];  
  203.         while ((count = gis.read(data, 0, BUFFER)) != -1) {  
  204.             os.write(data, 0, count);  
  205.         }  
  206.   
  207.         gis.close();  
  208.     }  
  209.   
  210.     /**
  211.      * 文件解压缩
  212.      *  
  213.      * @param path
  214.      * @throws Exception
  215.      */  
  216.     public static void decompress(String path) throws Exception {  
  217.         decompress(path, true);  
  218.     }  
  219.   
  220.     /**
  221.      * 文件解压缩
  222.      *  
  223.      * @param path
  224.      * @param delete
  225.      *            是否删除原始文件
  226.      * @throws Exception
  227.      */  
  228.     public static void decompress(String path, boolean delete) throws Exception {  
  229.         File file = new File(path);  
  230.         decompress(file, delete);  
  231.     }  
  232.   
  233. }  
复制代码
对应再来个测试用例,测试用例如下所示:
  1. /**
  2. * 2010-4-13
  3. */  
  4. package org.zlex.commons.compress.compress;  
  5.   
  6. import static org.junit.Assert.assertEquals;  
  7.   
  8. import java.io.DataInputStream;  
  9. import java.io.File;  
  10. import java.io.FileInputStream;  
  11. import java.io.FileOutputStream;  
  12.   
  13. import org.junit.Test;  
  14.   
  15. /**
  16. * BZip2
  17. *  
  18. * @author <a href="mailto:zlex.dongliang@gmail.com">梁栋</a>
  19. * @since 1.0
  20. */  
  21. public class BZip2UtilsTest {  
  22.   
  23.     private String inputStr = "zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org";  
  24.   
  25.     @Test  
  26.     public final void testDataCompress() throws Exception {  
  27.   
  28.         byte[] input = inputStr.getBytes();  
  29.         System.err.println("原文:\t" + inputStr);  
  30.         System.err.println("长度:\t" + input.length);  
  31.   
  32.         byte[] data = BZip2Utils.compress(input);  
  33.         System.err.println("压缩后:\t");  
  34.         System.err.println("长度:\t" + data.length);  
  35.   
  36.         byte[] output = BZip2Utils.decompress(data);  
  37.         String outputStr = new String(output);  
  38.         System.err.println("解压缩后:\t" + outputStr);  
  39.         System.err.println("长度:\t" + output.length);  
  40.   
  41.         assertEquals(inputStr, outputStr);  
  42.   
  43.     }  
  44.   
  45.     @Test  
  46.     public final void testFileCompress() throws Exception {  
  47.   
  48.         FileOutputStream fos = new FileOutputStream("d:/f.txt");  
  49.   
  50.         fos.write(inputStr.getBytes());  
  51.         fos.flush();  
  52.         fos.close();  
  53.   
  54.         BZip2Utils.compress("d:/f.txt");  
  55.   
  56.         BZip2Utils.decompress("d:/f.txt.bz2");  
  57.   
  58.         File file = new File("d:/f.txt");  
  59.   
  60.         FileInputStream fis = new FileInputStream(file);  
  61.   
  62.         DataInputStream dis = new DataInputStream(fis);  
  63.   
  64.         byte[] data = new byte[(int) file.length()];  
  65.         dis.readFully(data);  
  66.   
  67.         fis.close();  
  68.   
  69.         String outputStr = new String(data);  
  70.         assertEquals(inputStr, outputStr);  
  71.     }  
  72. }  
复制代码
虽然,两种算法在代码实现上几乎没有什么差别,但在压缩上想要看到效果,还真让我费了点事!
控制台输出如下所示:
  1. 原文: zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org
  2. 长度:        529
  3. 压缩后:        
  4. 长度:        76
  5. 解压缩后: zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org,zlex@zlex.org,snowolf@zlex.org,zlex.snowolf@zlex.org
  6. 长度:        529
复制代码
529字节-->76字节!
GZIP本身不需要太长的内容,经过压缩就能体现出压缩效果,而BZip2则需要压缩很长的内容时,才能体现其压缩效果,这说明BZip2更适合大数据压缩?!

Commons Compress不仅支持BZip2算法实现,同时也支持GZip算法实现。对于GZip算法实现,与Java原生实现基本上没有什么差别。其源代码分析,仅仅是做了简单的包装。
不过有必要提及的一点是,Commons Compress为压缩(GZip和BZip2)构建了压缩算法工厂类CompressorStreamFactory。通过这个类可以方便的构建GZip和BZip2的输入输出流,关键字分别为“gz”和“bzip2”。

GZip
  1. // GzipCompressorInputStream        
  2. CompressorInputStream gzipIn = new CompressorStreamFactory()  
  3.         .createCompressorInputStream("gz", is);  
  4.   
  5. // GzipCompressorOutputStream  
  6. CompressorOutputStream gzipOut = new CompressorStreamFactory()  
  7.         .createCompressorOutputStream("gz", os);  
复制代码
BZip2
  1. // BZip2CompressorInputStream  
  2. CompressorInputStream bzip2In = new CompressorStreamFactory()  
  3.         .createCompressorInputStream("bzip2", is);  
  4.   
  5. // BZip2CompressorOutputStream  
  6. CompressorOutputStream bzip2Out = new CompressorStreamFactory()  
  7.         .createCompressorOutputStream("bzip2", os);  
复制代码
GZip和BZip2在算法实现步骤上基本上没有什么差别,如果有必要统一,可按上述代码实现!


完整代码详见附件!


[attach]358[/attach]



作者: 破晓    时间: 2015-1-11 13:46
Java压缩技术(七) TAR——Commons实现

在linux下,tar是一个归档命令。当然,如果配合gzip、bzip2就可以达到归档+压缩的效果!
我们通过tar获得归档压缩文件其实恰恰包含了归档压缩两个操作,并且其操作次序也是先做归档操作,再做压缩操作! 通常我们忽略了归档的概念,将归档压缩文件简称为压缩文件!~


顺便复习一遍linux命令:
tar cf <file.tar> <file>将由文件<file>创建名为<file.tar>归档文件,同时保留原文件。
tar xf <file.tar>将由归档文件<file.tar>创建名为<file>的文件/目录,同时保留原文件。

对于归档压缩,需分为gzip和bzip2,相应的linux为:
gzip
tar czf <file.tar.gz> <file>将由文件<file>创建名为<file.tar.gz>归档压缩文件,同时保留原文件。
tar xzf <file.tar.gz>将由归档压缩文件<file.tar.gz>创建名为<file>的文件/目录,同时保留原文件。

bzip2
tar cjf <file.tar.bz2> <file>将由文件<file>创建名为<file.tar.bz2>归档压缩文件,同时保留原文件。
tar xjf <file.tar.bz2>将由归档压缩文件<file.tar.bz2>创建名为<file>的文件/目录,同时保留原文件。

今天的主角是Apache Commons Compress下用于Tar操作的三元干将
TarArchiveEntry 类似于Java 原生的ZipEntry,可以理解为Tar归档添加项。
TarArchiveOutputStream Tar归档输出流,用于归档。
TarArchiveInputStream Tar归档输入流,用于解归档。

至于jar,其实现方式与tar非常接近,我就不在这里废话了!
JarArchiveEntry 类似于Java 原生的ZipEntry,可以理解为Jar归档添加项。
JarArchiveOutputStream Jar归档输出流,用于归档。
JarArchiveInputStream Jar归档输入流,用于解归档。

读过Java压缩技术(二)Java压缩技术(三)会发现,其实Tar的实现与Java原生的Zip实现方式基本上没有差别!
先说归档,依旧需要考虑待归档的对象是文件还是目录:
  1. /**
  2. * 归档
  3. *  
  4. * @param srcFile
  5. *            源路径
  6. * @param taos
  7. *            TarArchiveOutputStream
  8. * @param basePath
  9. *            归档包内相对路径
  10. * @throws Exception
  11. */  
  12. private static void archive(File srcFile, TarArchiveOutputStream taos,  
  13.         String basePath) throws Exception {  
  14.     if (srcFile.isDirectory()) {  
  15.         archiveDir(srcFile, taos, basePath);  
  16.     } else {  
  17.         archiveFile(srcFile, taos, basePath);  
  18.     }  
  19. }  
复制代码
对于目录,需要区分空目录和包含文件的目录。
如果是空目录,只要简单追加一个归档项(TarArchiveEntry)即可,但注意其名字的结尾需要使用"/"作为区分目录的标识符(String PATH = "/";)。
如果是带有子文件的目录,则需要对其迭代归档:
  1. /**
  2. * 目录归档
  3. *  
  4. * @param dir
  5. * @param taos
  6. *            TarArchiveOutputStream
  7. * @param basePath
  8. * @throws Exception
  9. */  
  10. private static void archiveDir(File dir, TarArchiveOutputStream taos,  
  11.         String basePath) throws Exception {  
  12.   
  13.     File[] files = dir.listFiles();  
  14.   
  15.     if (files.length < 1) {  
  16.         TarArchiveEntry entry = new TarArchiveEntry(basePath  
  17.                 + dir.getName() + PATH);  
  18.   
  19.         taos.putArchiveEntry(entry);  
  20.         taos.closeArchiveEntry();  
  21.     }  
  22.   
  23.     for (File file : files) {  
  24.   
  25.         // 递归归档  
  26.         archive(file, taos, basePath + dir.getName() + PATH);  
  27.   
  28.     }  
  29. }  
复制代码
最后,来看归档操作:
  1. /**
  2. * 数据归档
  3. *  
  4. * @param data
  5. *            待归档数据
  6. * @param path
  7. *            归档数据的当前路径
  8. * @param name
  9. *            归档文件名
  10. * @param taos
  11. *            TarArchiveOutputStream
  12. * @throws Exception
  13. */  
  14. private static void archiveFile(File file, TarArchiveOutputStream taos,  
  15.         String dir) throws Exception {  
  16.   
  17.     TarArchiveEntry entry = new TarArchiveEntry(dir + file.getName());  
  18.   
  19.     entry.setSize(file.length());  
  20.   
  21.     taos.putArchiveEntry(entry);  
  22.   
  23.     BufferedInputStream bis = new BufferedInputStream(new FileInputStream(  
  24.             file));  
  25.     int count;  
  26.     byte data[] = new byte[BUFFER];  
  27.     while ((count = bis.read(data, 0, BUFFER)) != -1) {  
  28.         taos.write(data, 0, count);  
  29.     }  
  30.   
  31.     bis.close();  
  32.   
  33.     taos.closeArchiveEntry();  
  34. }  
复制代码
注意执行归档操作后,执行closeArchiveEntry()方法。
Tar解归档,与Zip解压相似,一样要遍历获得归档项:
  1. /**
  2. * 文件 解归档
  3. *  
  4. * @param destFile
  5. *            目标文件
  6. * @param tais
  7. *            ZipInputStream
  8. * @throws Exception
  9. */  
  10. private static void dearchive(File destFile, TarArchiveInputStream tais)  
  11.         throws Exception {  
  12.   
  13.     TarArchiveEntry entry = null;  
  14.     while ((entry = tais.getNextTarEntry()) != null) {  
  15.   
  16.         // 文件  
  17.         String dir = destFile.getPath() + File.separator + entry.getName();  
  18.   
  19.         File dirFile = new File(dir);  
  20.   
  21.         // 文件检查  
  22.         fileProber(dirFile);  
  23.   
  24.         if (entry.isDirectory()) {  
  25.             dirFile.mkdirs();  
  26.         } else {  
  27.             dearchiveFile(dirFile, tais);  
  28.         }  
  29.   
  30.     }  
  31. }  
复制代码
最后,进行解归档:
  1. /**
  2. * 文件解归档
  3. *  
  4. * @param destFile
  5. *            目标文件
  6. * @param tais
  7. *            TarArchiveInputStream
  8. * @throws Exception
  9. */  
  10. private static void dearchiveFile(File destFile, TarArchiveInputStream tais)  
  11.         throws Exception {  
  12.   
  13.     BufferedOutputStream bos = new BufferedOutputStream(  
  14.             new FileOutputStream(destFile));  
  15.   
  16.     int count;  
  17.     byte data[] = new byte[BUFFER];  
  18.     while ((count = tais.read(data, 0, BUFFER)) != -1) {  
  19.         bos.write(data, 0, count);  
  20.     }  
  21.   
  22.     bos.close();  
  23. }  
复制代码
文件探针用于构建父目录:
  1. /**
  2. * 文件探针
  3. *  
  4. * <pre>
  5. * 当父目录不存在时,创建目录!
  6. * </pre>
  7. *  
  8. * @param dirFile
  9. */  
  10. private static void fileProber(File dirFile) {  
  11.   
  12.     File parentFile = dirFile.getParentFile();  
  13.     if (!parentFile.exists()) {  
  14.   
  15.         // 递归寻找上级目录  
  16.         fileProber(parentFile);  
  17.   
  18.         parentFile.mkdir();  
  19.     }  
  20.   
  21. }  
复制代码
给出完整实现:
  1. /**
  2. * 2010-4-20
  3. */  
  4. package org.zlex.commons.compress;  
  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.   
  12. import org.apache.commons.compress.archivers.tar.TarArchiveEntry;  
  13. import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;  
  14. import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;  
  15.   
  16. /**
  17. * TAR工具
  18. *  
  19. * @author <a href="mailto:zlex.dongliang@gmail.com">梁栋</a>
  20. * @since 1.0
  21. */  
  22. public abstract class TarUtils {  
  23.   
  24.     private static final String BASE_DIR = "";  
  25.   
  26.     // 符号"/"用来作为目录标识判断符  
  27.     private static final String PATH = "/";  
  28.     private static final int BUFFER = 1024;  
  29.   
  30.     private static final String EXT = ".tar";  
  31.   
  32.     /**
  33.      * 归档
  34.      *  
  35.      * @param srcPath
  36.      * @param destPath
  37.      * @throws Exception
  38.      */  
  39.     public static void archive(String srcPath, String destPath)  
  40.             throws Exception {  
  41.   
  42.         File srcFile = new File(srcPath);  
  43.   
  44.         archive(srcFile, destPath);  
  45.   
  46.     }  
  47.   
  48.     /**
  49.      * 归档
  50.      *  
  51.      * @param srcFile
  52.      *            源路径
  53.      * @param destPath
  54.      *            目标路径
  55.      * @throws Exception
  56.      */  
  57.     public static void archive(File srcFile, File destFile) throws Exception {  
  58.   
  59.         TarArchiveOutputStream taos = new TarArchiveOutputStream(  
  60.                 new FileOutputStream(destFile));  
  61.   
  62.         archive(srcFile, taos, BASE_DIR);  
  63.   
  64.         taos.flush();  
  65.         taos.close();  
  66.     }  
  67.   
  68.     /**
  69.      * 归档
  70.      *  
  71.      * @param srcFile
  72.      * @throws Exception
  73.      */  
  74.     public static void archive(File srcFile) throws Exception {  
  75.         String name = srcFile.getName();  
  76.         String basePath = srcFile.getParent();  
  77.         String destPath = basePath + name + EXT;  
  78.         archive(srcFile, destPath);  
  79.     }  
  80.   
  81.     /**
  82.      * 归档文件
  83.      *  
  84.      * @param srcFile
  85.      * @param destPath
  86.      * @throws Exception
  87.      */  
  88.     public static void archive(File srcFile, String destPath) throws Exception {  
  89.         archive(srcFile, new File(destPath));  
  90.     }  
  91.   
  92.     /**
  93.      * 归档
  94.      *  
  95.      * @param srcPath
  96.      * @throws Exception
  97.      */  
  98.     public static void archive(String srcPath) throws Exception {  
  99.         File srcFile = new File(srcPath);  
  100.   
  101.         archive(srcFile);  
  102.     }  
  103.   
  104.     /**
  105.      * 归档
  106.      *  
  107.      * @param srcFile
  108.      *            源路径
  109.      * @param taos
  110.      *            TarArchiveOutputStream
  111.      * @param basePath
  112.      *            归档包内相对路径
  113.      * @throws Exception
  114.      */  
  115.     private static void archive(File srcFile, TarArchiveOutputStream taos,  
  116.             String basePath) throws Exception {  
  117.         if (srcFile.isDirectory()) {  
  118.             archiveDir(srcFile, taos, basePath);  
  119.         } else {  
  120.             archiveFile(srcFile, taos, basePath);  
  121.         }  
  122.     }  
  123.   
  124.     /**
  125.      * 目录归档
  126.      *  
  127.      * @param dir
  128.      * @param taos
  129.      *            TarArchiveOutputStream
  130.      * @param basePath
  131.      * @throws Exception
  132.      */  
  133.     private static void archiveDir(File dir, TarArchiveOutputStream taos,  
  134.             String basePath) throws Exception {  
  135.   
  136.         File[] files = dir.listFiles();  
  137.   
  138.         if (files.length < 1) {  
  139.             TarArchiveEntry entry = new TarArchiveEntry(basePath  
  140.                     + dir.getName() + PATH);  
  141.   
  142.             taos.putArchiveEntry(entry);  
  143.             taos.closeArchiveEntry();  
  144.         }  
  145.   
  146.         for (File file : files) {  
  147.   
  148.             // 递归归档  
  149.             archive(file, taos, basePath + dir.getName() + PATH);  
  150.   
  151.         }  
  152.     }  
  153.   
  154.     /**
  155.      * 数据归档
  156.      *  
  157.      * @param data
  158.      *            待归档数据
  159.      * @param path
  160.      *            归档数据的当前路径
  161.      * @param name
  162.      *            归档文件名
  163.      * @param taos
  164.      *            TarArchiveOutputStream
  165.      * @throws Exception
  166.      */  
  167.     private static void archiveFile(File file, TarArchiveOutputStream taos,  
  168.             String dir) throws Exception {  
  169.   
  170.         /**
  171.          * 归档内文件名定义
  172.          *  
  173.          * <pre>
  174.          * 如果有多级目录,那么这里就需要给出包含目录的文件名
  175.          * 如果用WinRAR打开归档包,中文名将显示为乱码
  176.          * </pre>
  177.          */  
  178.         TarArchiveEntry entry = new TarArchiveEntry(dir + file.getName());  
  179.   
  180.         entry.setSize(file.length());  
  181.   
  182.         taos.putArchiveEntry(entry);  
  183.   
  184.         BufferedInputStream bis = new BufferedInputStream(new FileInputStream(  
  185.                 file));  
  186.         int count;  
  187.         byte data[] = new byte[BUFFER];  
  188.         while ((count = bis.read(data, 0, BUFFER)) != -1) {  
  189.             taos.write(data, 0, count);  
  190.         }  
  191.   
  192.         bis.close();  
  193.   
  194.         taos.closeArchiveEntry();  
  195.     }  
  196.   
  197.     /**
  198.      * 解归档
  199.      *  
  200.      * @param srcFile
  201.      * @throws Exception
  202.      */  
  203.     public static void dearchive(File srcFile) throws Exception {  
  204.         String basePath = srcFile.getParent();  
  205.         dearchive(srcFile, basePath);  
  206.     }  
  207.   
  208.     /**
  209.      * 解归档
  210.      *  
  211.      * @param srcFile
  212.      * @param destFile
  213.      * @throws Exception
  214.      */  
  215.     public static void dearchive(File srcFile, File destFile) throws Exception {  
  216.   
  217.         TarArchiveInputStream tais = new TarArchiveInputStream(  
  218.                 new FileInputStream(srcFile));  
  219.         dearchive(destFile, tais);  
  220.   
  221.         tais.close();  
  222.   
  223.     }  
  224.   
  225.     /**
  226.      * 解归档
  227.      *  
  228.      * @param srcFile
  229.      * @param destPath
  230.      * @throws Exception
  231.      */  
  232.     public static void dearchive(File srcFile, String destPath)  
  233.             throws Exception {  
  234.         dearchive(srcFile, new File(destPath));  
  235.   
  236.     }  
  237.   
  238.     /**
  239.      * 文件 解归档
  240.      *  
  241.      * @param destFile
  242.      *            目标文件
  243.      * @param tais
  244.      *            ZipInputStream
  245.      * @throws Exception
  246.      */  
  247.     private static void dearchive(File destFile, TarArchiveInputStream tais)  
  248.             throws Exception {  
  249.   
  250.         TarArchiveEntry entry = null;  
  251.         while ((entry = tais.getNextTarEntry()) != null) {  
  252.   
  253.             // 文件  
  254.             String dir = destFile.getPath() + File.separator + entry.getName();  
  255.   
  256.             File dirFile = new File(dir);  
  257.   
  258.             // 文件检查  
  259.             fileProber(dirFile);  
  260.   
  261.             if (entry.isDirectory()) {  
  262.                 dirFile.mkdirs();  
  263.             } else {  
  264.                 dearchiveFile(dirFile, tais);  
  265.             }  
  266.   
  267.         }  
  268.     }  
  269.   
  270.     /**
  271.      * 文件 解归档
  272.      *  
  273.      * @param srcPath
  274.      *            源文件路径
  275.      *  
  276.      * @throws Exception
  277.      */  
  278.     public static void dearchive(String srcPath) throws Exception {  
  279.         File srcFile = new File(srcPath);  
  280.   
  281.         dearchive(srcFile);  
  282.     }  
  283.   
  284.     /**
  285.      * 文件 解归档
  286.      *  
  287.      * @param srcPath
  288.      *            源文件路径
  289.      * @param destPath
  290.      *            目标文件路径
  291.      * @throws Exception
  292.      */  
  293.     public static void dearchive(String srcPath, String destPath)  
  294.             throws Exception {  
  295.   
  296.         File srcFile = new File(srcPath);  
  297.         dearchive(srcFile, destPath);  
  298.     }  
  299.   
  300.     /**
  301.      * 文件解归档
  302.      *  
  303.      * @param destFile
  304.      *            目标文件
  305.      * @param tais
  306.      *            TarArchiveInputStream
  307.      * @throws Exception
  308.      */  
  309.     private static void dearchiveFile(File destFile, TarArchiveInputStream tais)  
  310.             throws Exception {  
  311.   
  312.         BufferedOutputStream bos = new BufferedOutputStream(  
  313.                 new FileOutputStream(destFile));  
  314.   
  315.         int count;  
  316.         byte data[] = new byte[BUFFER];  
  317.         while ((count = tais.read(data, 0, BUFFER)) != -1) {  
  318.             bos.write(data, 0, count);  
  319.         }  
  320.   
  321.         bos.close();  
  322.     }  
  323.   
  324.     /**
  325.      * 文件探针
  326.      *  
  327.      * <pre>
  328.      * 当父目录不存在时,创建目录!
  329.      * </pre>
  330.      *  
  331.      * @param dirFile
  332.      */  
  333.     private static void fileProber(File dirFile) {  
  334.   
  335.         File parentFile = dirFile.getParentFile();  
  336.         if (!parentFile.exists()) {  
  337.   
  338.             // 递归寻找上级目录  
  339.             fileProber(parentFile);  
  340.   
  341.             parentFile.mkdir();  
  342.         }  
  343.   
  344.     }  
  345.   
  346. }  
复制代码
最后给出测试用例:
  1. /**
  2. * 2010-4-20
  3. */  
  4. package org.zlex.commons.compress;  
  5.   
  6. import static org.junit.Assert.*;  
  7.   
  8. import java.io.DataInputStream;  
  9. import java.io.File;  
  10. import java.io.FileInputStream;  
  11. import java.io.FileOutputStream;  
  12.   
  13. import org.junit.Before;  
  14. import org.junit.Test;  
  15.   
  16. /**
  17. * Tar测试
  18. *  
  19. * @author <a href="mailto:zlex.dongliang@gmail.com">梁栋</a>
  20. * @since 1.0
  21. */  
  22. public class TarUtilsTest {  
  23.     private String inputStr;  
  24.     private String name = "data.xml";  
  25.   
  26.     @Before  
  27.     public void before() {  
  28.         StringBuilder sb = new StringBuilder();  
  29.         sb.append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");  
  30.         sb.append("\r\n");  
  31.         sb.append("<dataGroup>");  
  32.         sb.append("\r\n\t");  
  33.         sb.append("<dataItem>");  
  34.         sb.append("\r\n\t\t");  
  35.         sb.append("<data>");  
  36.         sb.append("Test");  
  37.         sb.append("</data>");  
  38.         sb.append("\r\n\t");  
  39.         sb.append("<dataItem>");  
  40.         sb.append("\r\n");  
  41.         sb.append("</dataGroup>");  
  42.   
  43.         inputStr = sb.toString();  
  44.     }  
  45.   
  46.     @Test  
  47.     public void testArchiveFile() throws Exception {  
  48.   
  49.         byte[] contentOfEntry = inputStr.getBytes();  
  50.   
  51.         String path = "d:/" + name;  
  52.   
  53.         FileOutputStream fos = new FileOutputStream(path);  
  54.   
  55.         fos.write(contentOfEntry);  
  56.         fos.flush();  
  57.         fos.close();  
  58.   
  59.         TarUtils.archive(path);  
  60.   
  61.         TarUtils.dearchive(path + ".tar");  
  62.   
  63.         File file = new File(path);  
  64.   
  65.         FileInputStream fis = new FileInputStream(file);  
  66.   
  67.         DataInputStream dis = new DataInputStream(fis);  
  68.   
  69.         byte[] data = new byte[(int) file.length()];  
  70.   
  71.         dis.readFully(data);  
  72.   
  73.         fis.close();  
  74.   
  75.         String outputStr = new String(data);  
  76.         assertEquals(inputStr, outputStr);  
  77.   
  78.     }  
  79.   
  80.     @Test  
  81.     public void testArchiveDir() throws Exception {  
  82.         String path = "d:/fd";  
  83.         TarUtils.archive(path);  
  84.   
  85.         TarUtils.dearchive(path + ".tar", "d:/fds");  
  86.     }  
  87.   
  88. }  
复制代码
执行代码,来看下效果:


[attach]359[/attach]

这是原始文件。


[attach]360[/attach]

这是归档后的文件。
注意红框,这里没有经过任何压缩!
除了tar、zip,其实还有很多归档算法,如ar、jar、cpio。其实现方式,与上述内容较为接近。
至于压缩成*.tar.gz、*.tar.bz2,请朋友们参照前几篇内容!

完整实现见附件!



[attach]361[/attach]


作者: 水玲珑    时间: 2015-1-11 17:10
顶........
作者: 我是一个兵    时间: 2016-8-17 16:58
看看。。。。。。。。。




欢迎光临 守望者--AIR技术交流 (http://www.airmyth.com/)