Java对Zip格式压缩和解压缩

ZIP 和 GZIP 的区别

gzip 是一种文件压缩工具(或该压缩工具产生的压缩文件格式),它的设计目标是处理单个的文件。gzip 在压缩文件中的数据时使用的就是 zlib。为了保存与文件属性有关的信息,gzip 需要在压缩文件(*.gz)中保存更多的头信息内容,而 zlib 不用考虑这一点。但 gzip 只适用于单个文件,所以我们在 UNIX/Linux 上经常看到的压缩包后缀都是 *.tar.gz 或 *.tgz,也就是先用 tar 把多个文件打包成单个文件,再用 gzip 压缩的结果。

zip 只是一种数据结构,跟 rar 同类型。zip 是适用于压缩多个文件的格式(相应的工具有 PkZip 和 WinZip 等),因此,zip 文件还要进一步包含文件目录结构的信息,比 gzip 的头信息更多。但需要注意,zip 格式可采用多种压缩算法,我们常见的 zip 文件大多不是用 zlib 的算法压缩的,其压缩数据的格式与 gzip 大不一样。

相关类与接口

Checksum:表示数据校验和的接口, 被类 Adler32 和 CRC32 实现
Adler32 :使用 Alder32 算法来计算 Checksum 数目
CRC32:使用 CRC32 算法来计算 Checksum 数目

CheckedInputStream:InputStream 派生类,可得到输入流的校验和 Checksum, 用于校验数据的完整性
CheckedOutputStream :OutputStream 派生类,可得到输出流的校验 Checksum, 用于校验数据的完整性

DeflaterOutputStream :压缩类的基类
ZipOutputStream:DeflaterOutputStream 的一个子类,把数据压缩成 Zip 文件格式
GZIPOutputStream :DeflaterOutputStream 的一个子类,把数据压缩成 GZip 文件格式

InflaterInputStream:解压缩类的基类
ZipInputStream:InflaterInputStream 的一个子类,能解压缩 Zip 格式的数据
GZIPInputStream:InflaterInputStream 的一个子类,能解压缩 Zip 格式的数据

ZipEntry:表示 ZIP 文件条目
ZipFile:此类用于从 ZIP 文件读取条目

压缩文件

下面实例我们使用了 apache 的 zip 工具包(所在包为 ant.jar ),因为 java 类型自带的不支持中文路径,不过两者使用的方式是一样的,只是 apache 压缩工具多了设置编码方式的接口,其他基本上是一样的。另外,如果使用 org.apache.tools.zip.ZipOutputStream 来压缩的话,我们只能使用 org.apache.tools.zip.ZipEntry 来解压,而不能使用 java.util.zip.ZipInputStream 来解压读取了,当然 apache 并未提供 ZipInputStream 类。

/**
 * 压缩
 */
public static void compress(String srcFilePath, String destFilePath) {

    File src = new File(srcFilePath);
    if (!src.exists()) {
        throw new RuntimeException(srcFilePath + "不存在");
    }

    File zipFile = new File(destFilePath);
    try {
        FileOutputStream fos = new FileOutputStream(zipFile);
        CheckedOutputStream cos = new CheckedOutputStream(fos, new CRC32());
        ZipOutputStream zos = new ZipOutputStream(cos);
        String baseDir = "";
        compressbyType(src, zos, baseDir);
        zos.close();
    } catch (Exception e) {
        e.printStackTrace();
    }

}

/**
 * 根据文件类型压缩
 */
private static void compressbyType(File src, ZipOutputStream zos, String baseDir) {

    if (!src.exists())
        return;

    System.out.println("压缩" + baseDir + src.getName());
    if (src.isFile()) {
        compressFile(src, zos, baseDir);
    } else if (src.isDirectory()) {
        compressDir(src, zos, baseDir);
    }

}

/**
 * 压缩文件
 */
private static void compressFile(File file, ZipOutputStream zos, String baseDir) {

    if (!file.exists())
        return;

    try {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
        ZipEntry entry = new ZipEntry(baseDir + file.getName());
        zos.putNextEntry(entry);

        int count;
        byte[] buf = new byte[BUFSIZE];
        while ((count = bis.read(buf)) != -1) {
            zos.write(buf, 0, count);
        }

        bis.close();
    } catch (Exception e) {
        e.printStackTrace();
    }

}

/**
 * 压缩文件夹
 */
private static void compressDir(File dir, ZipOutputStream zos, String baseDir) {

    if (!dir.exists())
        return;

    File[] files = dir.listFiles();
    if(files.length == 0){
        try {
            zos.putNextEntry(new ZipEntry(baseDir + dir.getName() + File.separator));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    for (File file : files) {
        compressbyType(file, zos, baseDir + dir.getName() + File.separator);
    }

}

总结步骤:

  1. 创建压缩到的文件 File zipFile = new File(destFilePath);

  2. 根据 zipFile 生成 ZipOutputStream 用于写入即将被压缩的文件

    FileOutputStream fos = new FileOutputStream(zipFile);

    CheckedOutputStream cos = new CheckedOutputStream(fos, new CRC32());

    ZipOutputStream zos = new ZipOutputStream(cos);

  3. 循环遍历源文件,首先需要创建 ZipEntry 用于标记压缩文件中含有的条目

    ZipEntry entry = new ZipEntry(baseDir + file.getName());

    然后将条目增加到 ZipOutputStream 中,zos.putNextEntry(entry);

    最后再调用要写入条目对应文件的输入流读取文件内容写入到压缩文件中。

    BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
    ZipEntry entry = new ZipEntry(baseDir + file.getName());
    zos.putNextEntry(entry);
    int count;
    byte[] buf = new byte[_BUFSIZE_];
    while ((count = bis.read(buf)) != -1) {
        zos.write(buf, 0, count);
    }

    注意:如果是空目录直接 zos.putNextEntry(new ZipEntry(baseDir +     dir.getName()+ File.separator)) 并不用写入文件内容,其中最主要的涉及到目录的压缩的,就是这一句话  out.putNextEntry(new ZipEntry(base + "/")); // 放入一级目录 (防止空目录被丢弃)

解压 zip 文件

/**
 * 解压缩
 * @param srcPath 	压缩文件路径
 * @param dest		解压路径
 */
public static void decompress(String srcPath, String dest) throws Exception {

    File file = new File(srcPath);
    if (!file.exists()) {
        throw new RuntimeException(srcPath + " 所指文件不存在 ");
    }

    ZipFile zf = new ZipFile(file);
    Enumeration entries = zf.getEntries();
    ZipEntry entry;

    while (entries.hasMoreElements()) {
        entry = (ZipEntry) entries.nextElement();
        System._out_.println(" 解压 " + entry.getName());

        if (entry.isDirectory()) {

            String dirPath = dest + File._separator_ + entry.getName();
            File dir = new File(dirPath);
            dir.mkdirs();

        } else {

            // 表示文件
            File f = new File(dest + File._separator_ + entry.getName());
            if (!f.exists()) {
                String dirs = FileUtils._getParentPath_(f);
                File parentDir = new File(dirs);
                parentDir.mkdirs();
            }

            f.createNewFile();
            // 将压缩文件内容写入到这个文件中
            InputStream is = zf.getInputStream(entry);
            FileOutputStream fos = new FileOutputStream(f);
            int count;
            byte[] buf = new byte[8192];
            while ((count = is.read(buf)) != -1) {
                fos.write(buf, 0, count);
            }
            is.close();
            fos.close();
        }
    }

}

原文地址 https://www.cnblogs.com/ljdblog/p/5844184.html