以前遇到文件压缩的功能是都直接从网上找一个,随便测试一下能用就行了。既不关心效率也没有好好的测一下是否支持内嵌文件夹的压缩。
现在仔细测试才发现网上好多都不支持内嵌文件夹的压缩。 支持的不是有问题就是速度比较慢。
框架里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倍多。这结果还算满意。 代码水平有限,只能到这儿了。如果有更精简的代码,欢迎指教。