Java 高效压缩zip

简介: 以前遇到文件压缩的功能是都直接从网上找一个,随便测试一下能用就行了。既不关心效率也没有好好的测一下是否支持内嵌文件夹的压缩。 现在仔细测试才发现网上好多都不支持内嵌文件夹的压缩。

以前遇到文件压缩的功能是都直接从网上找一个,随便测试一下能用就行了。既不关心效率也没有好好的测一下是否支持内嵌文件夹的压缩。

现在仔细测试才发现网上好多都不支持内嵌文件夹的压缩。 支持的不是有问题就是速度比较慢。

框架里jar包里的类诸如IOUtils,FileUtils里没有提供压缩的方法。连commons-compress.jar里都没有现成的。

我决定自己写一个。真写的时候发现不是那么容易。目标是 给定一个文件夹路径和一个输出路径,将给定文件夹里的所有东西都压缩到zip里。

在commons-compress的文档里发现有一个支持多线程压缩的类 ParallelScatterZipCreator。我决定试一试。

在github里找到 ScatterSample.java  和  ScatterSampleTest.java 这两个类。这个demo不支持带文件夹的压缩。 文档里也没有给出带文件夹压缩的demo。没办法只能仔细阅

读这个demo从api里找线索了。


仔细摸索后,代码如下:

package test;


import java.io.File;
import java.io.IOException;
import java.util.concurrent.ExecutionException;


import org.apache.commons.compress.archivers.zip.ParallelScatterZipCreator;
import org.apache.commons.compress.archivers.zip.ScatterZipOutputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntryRequest;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.parallel.InputStreamSupplier;


public class ScatterSample {
    private String rootPath;


    ParallelScatterZipCreator scatterZipCreator = new ParallelScatterZipCreator();
    // ParallelScatterZipCreator api says:
    // 注意这个类不保证写入到输出文件的顺序。需要保持特定顺序的(manifests,文件夹)必须使用这个类的客户类进行处理
    // 通常的做法是 在调用这个类的writeTo方法前把这些东西写入到ZipArchiveOutputStream
    ScatterZipOutputStream dirs = ScatterZipOutputStream
            .fileBased(File.createTempFile("whatever-preffix", ".whatever"));


    public ScatterSample(String rootPath) throws IOException {
        this.rootPath = rootPath;
    }


    public void addEntry(final ZipArchiveEntry zipArchiveEntry, final InputStreamSupplier streamSupplier)
            throws IOException {
        if (zipArchiveEntry.isDirectory() && !zipArchiveEntry.isUnixSymlink()) {
            dirs.addArchiveEntry(ZipArchiveEntryRequest.createZipArchiveEntryRequest(zipArchiveEntry, streamSupplier));
        } else {
            scatterZipCreator.addArchiveEntry(zipArchiveEntry, streamSupplier);
        }
    }


    public void writeTo(final ZipArchiveOutputStream zipArchiveOutputStream)
            throws IOException, ExecutionException, InterruptedException {
        dirs.writeTo(zipArchiveOutputStream);
        dirs.close();
        scatterZipCreator.writeTo(zipArchiveOutputStream);
    }


    public String getRootPath() {
        return rootPath;
    }


    public void setRootPath(String rootPath) {
        this.rootPath = rootPath;
    }


}


package test;


import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;


import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.parallel.InputStreamSupplier;
import org.apache.commons.io.input.NullInputStream;
import org.junit.Test;


public class ScatterSampleTest {


    @Test
    public void testSample() throws Exception {
        long begin = System.currentTimeMillis();
        final File result = new File("d:/test2/eclipse2.zip");
        createZipFile("F:/Java/eclipseplusUIx64_best", result);
        long end = System.currentTimeMillis();
        System.out.println("用时:" + (end - begin) + " ms");
    }


    class CustomInputStreamSupplier implements InputStreamSupplier {
        private File currentFile;


        public CustomInputStreamSupplier(File currentFile) {
            this.currentFile = currentFile;
        }


        @Override
        public InputStream get() {
            try {
                // InputStreamSupplier api says:
                // 返回值:输入流。永远不能为Null,但可以是一个空的流
                return currentFile.isDirectory() ? new NullInputStream(0) : new FileInputStream(currentFile);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            return null;
        }
    }


    private void addEntry(String entryName, File currentFile, ScatterSample scatterSample) throws IOException {
        ZipArchiveEntry archiveEntry = new ZipArchiveEntry(entryName);
        archiveEntry.setMethod(ZipEntry.DEFLATED);
        final InputStreamSupplier supp = new CustomInputStreamSupplier(currentFile);
        scatterSample.addEntry(archiveEntry, supp);
    }


    private void compressCurrentDirectory(File dir, ScatterSample scatterSample) throws IOException {
        if (dir == null) {
            throw new IOException("源路径不能为空!");
        }
        String relativePath = "";
        if (dir.isFile()) {
            relativePath = dir.getName();
            addEntry(relativePath, dir, scatterSample);
            return;
        }


        // 空文件夹
        if (dir.listFiles().length == 0) {
            relativePath = dir.getAbsolutePath().replace(scatterSample.getRootPath(), "");
            addEntry(relativePath + File.separator, dir, scatterSample);
            return;
        }
        for (File f : dir.listFiles()) {
            if (f.isDirectory()) {
                compressCurrentDirectory(f, scatterSample);
            } else {
                relativePath = f.getParent().replace(scatterSample.getRootPath(), "");
                addEntry(relativePath + File.separator + f.getName(), f, scatterSample);
            }
        }
    }


    private void createZipFile(final String rootPath, final File result) throws Exception {
        File dstFolder = new File(result.getParent());
        if (!dstFolder.isDirectory()) {
            dstFolder.mkdirs();
        }
        File rootDir = new File(rootPath);
        final ScatterSample scatterSample = new ScatterSample(rootDir.getAbsolutePath());
        compressCurrentDirectory(rootDir, scatterSample);
        final ZipArchiveOutputStream zipArchiveOutputStream = new ZipArchiveOutputStream(result);
        scatterSample.writeTo(zipArchiveOutputStream);
        zipArchiveOutputStream.close();
    }
}

以解压后的eclipse文件为例,用时:4624 ms。比较了一下网上其他的方法,快了2倍多。这结果还算满意。 代码水平有限,只能到这儿了。如果有更精简的代码,欢迎指教。

目录
相关文章
|
1月前
|
存储 Java API
Java实现导出多个excel表打包到zip文件中,供客户端另存为窗口下载
Java实现导出多个excel表打包到zip文件中,供客户端另存为窗口下载
41 4
|
2月前
|
安全 Java Android开发
JavaWeb解压缩漏洞之ZipSlip与Zip炸弹
JavaWeb解压缩漏洞之ZipSlip与Zip炸弹
66 5
|
3月前
|
安全 Java Android开发
JavaWeb解压缩漏洞之ZipSlip与Zip炸弹
JavaWeb解压缩漏洞之ZipSlip与Zip炸弹
120 2
|
3月前
|
算法 Java
Java 压缩文件
在Java中压缩文件是一个常见的需求,通常可以通过使用Java自带的`java.util.zip`包来实现。这个包提供了`ZipOutputStream`类来创建ZIP格式的压缩文件。以下是一个简单的示例,展示了如何将多个文件压缩到一个ZIP文件中。 ### 示例:将多个文件压缩到一个ZIP文件中 ```java import java.io.*; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class ZipFilesExample { public static vo
|
4月前
|
Java 大数据 测试技术
Java对象头压缩---- 永久为Java应用“降本增效”
本文介绍了一下OpenJDK的最新技术,对象头压缩,来大幅优化Java对象的内存占用。
|
4月前
|
Java
Java SpringBoot 7z 压缩、解压
Java SpringBoot 7z 压缩、解压
76 1
|
5月前
|
Java 运维
开发与运维技术问题之ava对象头压缩技术支持所有的Java垃圾回收器如何解决
开发与运维技术问题之ava对象头压缩技术支持所有的Java垃圾回收器如何解决
35 1
|
4月前
|
存储 安全 算法
Java中防止压缩炸弹的技术分享
【8月更文挑战第17天】在软件开发和数据处理的日常工作中,我们经常会遇到各种压缩文件。然而,一种被称为“压缩炸弹”的恶意文件却给开发者带来了不小的困扰。压缩炸弹通过特殊设计的压缩算法,使得极小的文件在解压后能膨胀到异常巨大的体积,从而消耗大量系统资源甚至导致系统崩溃。本文将围绕“如何在Java中防止压缩炸弹”这一主题,分享一些实用的技术干货。
133 0
|
5月前
|
算法 Java
Java面试题:解释垃圾回收中的标记-清除、复制、标记-压缩算法的工作原理
Java面试题:解释垃圾回收中的标记-清除、复制、标记-压缩算法的工作原理
66 1
|
5月前
|
算法 Java 程序员
Java面试题:解释Java的垃圾回收机制,包括常见的垃圾回收算法。介绍一下Java的垃圾回收算法中的标记-压缩算法。
Java面试题:解释Java的垃圾回收机制,包括常见的垃圾回收算法。介绍一下Java的垃圾回收算法中的标记-压缩算法。
52 0