Java压缩技术(七) TAR——Commons实现

顺便复习一遍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实现方式基本上没有差别!  
先说归档,依旧需要考虑待归档的对象是文件还是目录: 
Java代码   收藏代码
  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 = "/";)。 
如果是带有子文件的目录,则需要对其迭代归档: 
Java代码   收藏代码
  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. }  

最后,来看归档操作: 
Java代码   收藏代码
  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解压相似,一样要遍历获得归档项: 
Java代码   收藏代码
  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. }  

最后,进行解归档: 
Java代码   收藏代码
  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. }  

文件探针用于构建父目录: 
Java代码   收藏代码
  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. }  

给出完整实现: 
Java代码   收藏代码
  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:[email protected]">梁栋</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. }  


最后给出测试用例: 
Java代码   收藏代码
  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:[email protected]">梁栋</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. }  

执行代码,来看下效果: 
Java压缩技术(七) TAR——Commons实现_第1张图片  
这是原始文件。 
Java压缩技术(七) TAR——Commons实现_第2张图片  
这是归档后的文件。 
注意红框,这里没有经过任何压缩!  
除了tar、zip,其实还有很多归档算法,如ar、jar、cpio。其实现方式,与上述内容较为接近。 
至于压缩成*.tar.gz、*.tar.bz2,请朋友们参照前几篇内容!  

你可能感兴趣的:(java,压缩,技术,rar)