Java Commons实现文件的归档压缩

转载: https://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,请朋友们参照前几篇内容! 

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值