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

xiaoxiao2021-02-28  154

原文链接: http://snowolf.iteye.com/blog/648652

在linux下,tar是一个归档命令。当然,如果配合gzip、bzip2就可以达到归档+压缩的效果!  我们通过tar获得归档压缩文件其实恰恰包含了归档压缩两个操作,并且其操作次序也是先做归档操作,再做压缩操作! 通常我们忽略了归档的概念,将归档压缩文件简称为压缩文件!~  相关链接:  Java压缩技术(一) ZLib  Java压缩技术(二) ZIP压缩——Java原生实现  Java压缩技术(三) ZIP解压缩——Java原生实现  Java压缩技术(四) GZIP——Java原生实现  Java压缩技术(五) GZIP相关——浏览器解析  Java压缩技术(六) BZIP2——Commons实现  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代码   /**   * 归档   *    * @param srcFile   *            源路径   * @param taos   *            TarArchiveOutputStream   * @param basePath   *            归档包内相对路径   * @throws Exception   */   private static void archive(File srcFile, TarArchiveOutputStream taos,           String basePath) throws Exception {       if (srcFile.isDirectory()) {           archiveDir(srcFile, taos, basePath);       } else {           archiveFile(srcFile, taos, basePath);       }   }   对于目录,需要区分空目录和包含文件的目录。  如果是空目录,只要简单追加一个归档项(TarArchiveEntry)即可,但注意其名字的结尾需要使用"/"作为区分目录的标识符(String PATH = "/";)。  如果是带有子文件的目录,则需要对其迭代归档:  Java代码   /**   * 目录归档   *    * @param dir   * @param taos   *            TarArchiveOutputStream   * @param basePath   * @throws Exception   */   private static void archiveDir(File dir, TarArchiveOutputStream taos,           String basePath) throws Exception {          File[] files = dir.listFiles();          if (files.length < 1) {           TarArchiveEntry entry = new TarArchiveEntry(basePath                   + dir.getName() + PATH);              taos.putArchiveEntry(entry);           taos.closeArchiveEntry();       }          for (File file : files) {              // 递归归档           archive(file, taos, basePath + dir.getName() + PATH);          }   }   最后,来看归档操作:  Java代码   /**   * 数据归档   *    * @param data   *            待归档数据   * @param path   *            归档数据的当前路径   * @param name   *            归档文件名   * @param taos   *            TarArchiveOutputStream   * @throws Exception   */   private static void archiveFile(File file, TarArchiveOutputStream taos,           String dir) throws Exception {          TarArchiveEntry entry = new TarArchiveEntry(dir + file.getName());          entry.setSize(file.length());          taos.putArchiveEntry(entry);          BufferedInputStream bis = new BufferedInputStream(new FileInputStream(               file));       int count;       byte data[] = new byte[BUFFER];       while ((count = bis.read(data, 0, BUFFER)) != -1) {           taos.write(data, 0, count);       }          bis.close();          taos.closeArchiveEntry();   }   注意执行归档操作后,执行closeArchiveEntry()方法。  Tar解归档,与Zip解压相似,一样要遍历获得归档项:  Java代码   /**   * 文件 解归档   *    * @param destFile   *            目标文件   * @param tais   *            ZipInputStream   * @throws Exception   */   private static void dearchive(File destFile, TarArchiveInputStream tais)           throws Exception {          TarArchiveEntry entry = null;       while ((entry = tais.getNextTarEntry()) != null) {              // 文件           String dir = destFile.getPath() + File.separator + entry.getName();              File dirFile = new File(dir);              // 文件检查           fileProber(dirFile);              if (entry.isDirectory()) {               dirFile.mkdirs();           } else {               dearchiveFile(dirFile, tais);           }          }   }   最后,进行解归档:  Java代码   /**   * 文件解归档   *    * @param destFile   *            目标文件   * @param tais   *            TarArchiveInputStream   * @throws Exception   */   private static void dearchiveFile(File destFile, TarArchiveInputStream tais)           throws Exception {          BufferedOutputStream bos = new BufferedOutputStream(               new FileOutputStream(destFile));          int count;       byte data[] = new byte[BUFFER];       while ((count = tais.read(data, 0, BUFFER)) != -1) {           bos.write(data, 0, count);       }          bos.close();   }   文件探针用于构建父目录:  Java代码   /**   * 文件探针   *    * <pre>   * 当父目录不存在时,创建目录!   * </pre>   *    * @param dirFile   */   private static void fileProber(File dirFile) {          File parentFile = dirFile.getParentFile();       if (!parentFile.exists()) {              // 递归寻找上级目录           fileProber(parentFile);              parentFile.mkdir();       }      }   给出完整实现:  Java代码   /**   * 2010-4-20   */   package org.zlex.commons.compress;      import java.io.BufferedInputStream;   import java.io.BufferedOutputStream;   import java.io.File;   import java.io.FileInputStream;   import java.io.FileOutputStream;      import org.apache.commons.compress.archivers.tar.TarArchiveEntry;   import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;   import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;      /**   * TAR工具   *    * @author <a href="mailto:zlex.dongliang@gmail.com">梁栋</a>   * @since 1.0   */   public abstract class TarUtils {          private static final String BASE_DIR = "";          // 符号"/"用来作为目录标识判断符       private static final String PATH = "/";       private static final int BUFFER = 1024;          private static final String EXT = ".tar";          /**       * 归档       *        * @param srcPath       * @param destPath       * @throws Exception       */       public static void archive(String srcPath, String destPath)               throws Exception {              File srcFile = new File(srcPath);              archive(srcFile, destPath);          }          /**       * 归档       *        * @param srcFile       *            源路径       * @param destPath       *            目标路径       * @throws Exception       */       public static void archive(File srcFile, File destFile) throws Exception {              TarArchiveOutputStream taos = new TarArchiveOutputStream(                   new FileOutputStream(destFile));              archive(srcFile, taos, BASE_DIR);              taos.flush();           taos.close();       }          /**       * 归档       *        * @param srcFile       * @throws Exception       */       public static void archive(File srcFile) throws Exception {           String name = srcFile.getName();           String basePath = srcFile.getParent();           String destPath = basePath + name + EXT;           archive(srcFile, destPath);       }          /**       * 归档文件       *        * @param srcFile       * @param destPath       * @throws Exception       */       public static void archive(File srcFile, String destPath) throws Exception {           archive(srcFile, new File(destPath));       }          /**       * 归档       *        * @param srcPath       * @throws Exception       */       public static void archive(String srcPath) throws Exception {           File srcFile = new File(srcPath);              archive(srcFile);       }          /**       * 归档       *        * @param srcFile       *            源路径       * @param taos       *            TarArchiveOutputStream       * @param basePath       *            归档包内相对路径       * @throws Exception       */       private static void archive(File srcFile, TarArchiveOutputStream taos,               String basePath) throws Exception {           if (srcFile.isDirectory()) {               archiveDir(srcFile, taos, basePath);           } else {               archiveFile(srcFile, taos, basePath);           }       }          /**       * 目录归档       *        * @param dir       * @param taos       *            TarArchiveOutputStream       * @param basePath       * @throws Exception       */       private static void archiveDir(File dir, TarArchiveOutputStream taos,               String basePath) throws Exception {              File[] files = dir.listFiles();              if (files.length < 1) {               TarArchiveEntry entry = new TarArchiveEntry(basePath                       + dir.getName() + PATH);                  taos.putArchiveEntry(entry);               taos.closeArchiveEntry();           }              for (File file : files) {                  // 递归归档               archive(file, taos, basePath + dir.getName() + PATH);              }       }          /**       * 数据归档       *        * @param data       *            待归档数据       * @param path       *            归档数据的当前路径       * @param name       *            归档文件名       * @param taos       *            TarArchiveOutputStream       * @throws Exception       */       private static void archiveFile(File file, TarArchiveOutputStream taos,               String dir) throws Exception {              /**           * 归档内文件名定义           *            * <pre>           * 如果有多级目录,那么这里就需要给出包含目录的文件名           * 如果用WinRAR打开归档包,中文名将显示为乱码           * </pre>           */           TarArchiveEntry entry = new TarArchiveEntry(dir + file.getName());              entry.setSize(file.length());              taos.putArchiveEntry(entry);              BufferedInputStream bis = new BufferedInputStream(new FileInputStream(                   file));           int count;           byte data[] = new byte[BUFFER];           while ((count = bis.read(data, 0, BUFFER)) != -1) {               taos.write(data, 0, count);           }              bis.close();              taos.closeArchiveEntry();       }          /**       * 解归档       *        * @param srcFile       * @throws Exception       */       public static void dearchive(File srcFile) throws Exception {           String basePath = srcFile.getParent();           dearchive(srcFile, basePath);       }          /**       * 解归档       *        * @param srcFile       * @param destFile       * @throws Exception       */       public static void dearchive(File srcFile, File destFile) throws Exception {              TarArchiveInputStream tais = new TarArchiveInputStream(                   new FileInputStream(srcFile));           dearchive(destFile, tais);              tais.close();          }          /**       * 解归档       *        * @param srcFile       * @param destPath       * @throws Exception       */       public static void dearchive(File srcFile, String destPath)               throws Exception {           dearchive(srcFile, new File(destPath));          }          /**       * 文件 解归档       *        * @param destFile       *            目标文件       * @param tais       *            ZipInputStream       * @throws Exception       */       private static void dearchive(File destFile, TarArchiveInputStream tais)               throws Exception {              TarArchiveEntry entry = null;           while ((entry = tais.getNextTarEntry()) != null) {                  // 文件               String dir = destFile.getPath() + File.separator + entry.getName();                  File dirFile = new File(dir);                  // 文件检查               fileProber(dirFile);                  if (entry.isDirectory()) {                   dirFile.mkdirs();               } else {                   dearchiveFile(dirFile, tais);               }              }       }          /**       * 文件 解归档       *        * @param srcPath       *            源文件路径       *        * @throws Exception       */       public static void dearchive(String srcPath) throws Exception {           File srcFile = new File(srcPath);              dearchive(srcFile);       }          /**       * 文件 解归档       *        * @param srcPath       *            源文件路径       * @param destPath       *            目标文件路径       * @throws Exception       */       public static void dearchive(String srcPath, String destPath)               throws Exception {              File srcFile = new File(srcPath);           dearchive(srcFile, destPath);       }          /**       * 文件解归档       *        * @param destFile       *            目标文件       * @param tais       *            TarArchiveInputStream       * @throws Exception       */       private static void dearchiveFile(File destFile, TarArchiveInputStream tais)               throws Exception {              BufferedOutputStream bos = new BufferedOutputStream(                   new FileOutputStream(destFile));              int count;           byte data[] = new byte[BUFFER];           while ((count = tais.read(data, 0, BUFFER)) != -1) {               bos.write(data, 0, count);           }              bos.close();       }          /**       * 文件探针       *        * <pre>       * 当父目录不存在时,创建目录!       * </pre>       *        * @param dirFile       */       private static void fileProber(File dirFile) {              File parentFile = dirFile.getParentFile();           if (!parentFile.exists()) {                  // 递归寻找上级目录               fileProber(parentFile);                  parentFile.mkdir();           }          }      }   最后给出测试用例:  Java代码   /**   * 2010-4-20   */   package org.zlex.commons.compress;      import static org.junit.Assert.*;      import java.io.DataInputStream;   import java.io.File;   import java.io.FileInputStream;   import java.io.FileOutputStream;      import org.junit.Before;   import org.junit.Test;      /**   * Tar测试   *    * @author <a href="mailto:zlex.dongliang@gmail.com">梁栋</a>   * @since 1.0   */   public class TarUtilsTest {       private String inputStr;       private String name = "data.xml";          @Before       public void before() {           StringBuilder sb = new StringBuilder();           sb.append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");           sb.append("\r\n");           sb.append("<dataGroup>");           sb.append("\r\n\t");           sb.append("<dataItem>");           sb.append("\r\n\t\t");           sb.append("<data>");           sb.append("Test");           sb.append("</data>");           sb.append("\r\n\t");           sb.append("<dataItem>");           sb.append("\r\n");           sb.append("</dataGroup>");              inputStr = sb.toString();       }          @Test       public void testArchiveFile() throws Exception {              byte[] contentOfEntry = inputStr.getBytes();              String path = "d:/" + name;              FileOutputStream fos = new FileOutputStream(path);              fos.write(contentOfEntry);           fos.flush();           fos.close();              TarUtils.archive(path);              TarUtils.dearchive(path + ".tar");              File file = new File(path);              FileInputStream fis = new FileInputStream(file);              DataInputStream dis = new DataInputStream(fis);              byte[] data = new byte[(int) file.length()];              dis.readFully(data);              fis.close();              String outputStr = new String(data);           assertEquals(inputStr, outputStr);          }          @Test       public void testArchiveDir() throws Exception {           String path = "d:/fd";           TarUtils.archive(path);              TarUtils.dearchive(path + ".tar""d:/fds");       }      }   执行代码,来看下效果:    这是原始文件。    这是归档后的文件。  注意红框,这里没有经过任何压缩!   除了tar、zip,其实还有很多归档算法,如ar、jar、cpio。其实现方式,与上述内容较为接近。  至于压缩成*.tar.gz、*.tar.bz2,请朋友们参照前几篇内容!  

转载请注明原文地址: https://www.6miu.com/read-33677.html

最新回复(0)