什么是一个好的Java库来压缩/解压缩文件?

我查看了JDK和Apache压缩库附带的默认Zip库,我对它们不满意,原因有3个:

  1. 它们臃肿不堪,API设计也很糟糕。我必须写50行锅炉板字节数组输出,压缩输入,文件输出流和关闭相关流和捕捉异常和移动字节缓冲区对我自己?为什么我不能有一个简单的API,看起来像这样Zipper.unzip(InputStream zipFile, File targetDirectory, String password = null)Zipper.zip(File targetDirectory, String password = null)只是工作?

  2. 它似乎压缩解压缩破坏文件元数据和密码处理是坏的。

  3. 而且,我所尝试的所有库都比我用UNIX获得的命令行压缩工具慢2-3倍?

对我来说(2)和(3)是次要的点,但我真的想要一个良好的测试库与一行接口。

248762 次浏览

你看了http://commons.apache.org/vfs/吗?它声称可以为你简化很多事情。但我从未在项目中使用过。

除了JDK或Apache压缩之外,我也不知道Java-Native压缩库。

我记得有一次我们从Apache Ant中剥离了一些特性——它们有很多内置的压缩/解压utils。

VFS的示例代码如下所示:

File zipFile = ...;
File outputDir = ...;
FileSystemManager fsm = VFS.getManager();
URI zip = zipFile.toURI();
FileObject packFileObject = fsm.resolveFile(packLocation.toString());
FileObject to = fsm.toFileObject(destDir);
FileObject zipFS;
try {
zipFS = fsm.createFileSystem(packFileObject);
fsm.toFileObject(outputDir).copyFrom(zipFS, new AllFileSelector());
} finally {
zipFS.close();
}

解压zip文件及其所有子文件夹,只使用JDK:

private void extractFolder(String zipFile,String extractFolder)
{
try
{
int BUFFER = 2048;
File file = new File(zipFile);


ZipFile zip = new ZipFile(file);
String newPath = extractFolder;


new File(newPath).mkdir();
Enumeration zipFileEntries = zip.entries();


// Process each entry
while (zipFileEntries.hasMoreElements())
{
// grab a zip file entry
ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
String currentEntry = entry.getName();


File destFile = new File(newPath, currentEntry);
//destFile = new File(newPath, destFile.getName());
File destinationParent = destFile.getParentFile();


// create the parent directory structure if needed
destinationParent.mkdirs();


if (!entry.isDirectory())
{
BufferedInputStream is = new BufferedInputStream(zip
.getInputStream(entry));
int currentByte;
// establish buffer for writing file
byte data[] = new byte[BUFFER];


// write the current file to disk
FileOutputStream fos = new FileOutputStream(destFile);
BufferedOutputStream dest = new BufferedOutputStream(fos,
BUFFER);


// read and write until last byte is encountered
while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
dest.write(data, 0, currentByte);
}
dest.flush();
dest.close();
is.close();
}




}
}
catch (Exception e)
{
Log("ERROR: "+e.getMessage());
}


}

Zip文件及其所有子文件夹:

 private void addFolderToZip(File folder, ZipOutputStream zip, String baseName) throws IOException {
File[] files = folder.listFiles();
for (File file : files) {
if (file.isDirectory()) {
addFolderToZip(file, zip, baseName);
} else {
String name = file.getAbsolutePath().substring(baseName.length());
ZipEntry zipEntry = new ZipEntry(name);
zip.putNextEntry(zipEntry);
IOUtils.copy(new FileInputStream(file), zip);
zip.closeEntry();
}
}
}

TrueZip是一个非常好的项目。

TrueZIP是一个基于Java的虚拟文件系统(VFS)插件框架,它提供对归档文件的透明访问,就像它们只是普通的目录一样

例如(来自网站):

File file = new TFile("archive.tar.gz/README.TXT");
OutputStream out = new TFileOutputStream(file);
try {
// Write archive entry contents here.
...
} finally {
out.close();
}

在Java 8中,使用Apache Commons-IOIOUtils,你可以这样做:

try (java.util.zip.ZipFile zipFile = new ZipFile(file)) {
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
File entryDestination = new File(outputDir,  entry.getName());
if (entry.isDirectory()) {
entryDestination.mkdirs();
} else {
entryDestination.getParentFile().mkdirs();
try (InputStream in = zipFile.getInputStream(entry);
OutputStream out = new FileOutputStream(entryDestination)) {
IOUtils.copy(in, out);
}
}
}
}

它仍然是一些样板代码,但它只有一个非外来依赖项:Commons-IO

在Java 11及更高版本中,可能会有更好的选择,请参阅ZhekaKozlov的评论。

我知道已经晚了,有很多答案,但这个zip4j是我使用过的最好的压缩库之一。它很简单(没有锅炉代码),可以很容易地处理密码保护文件。

import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.core.ZipFile;




public static void unzip(){
String source = "some/compressed/file.zip";
String destination = "some/destination/folder";
String password = "password";


try {
ZipFile zipFile = new ZipFile(source);
if (zipFile.isEncrypted()) {
zipFile.setPassword(password);
}
zipFile.extractAll(destination);
} catch (ZipException e) {
e.printStackTrace();
}
}

Maven依赖项是:

<dependency>
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
<version>1.3.2</version>
</dependency>

你可以查看的另一个选项是zt-zip,可从Maven中心和项目页面https://github.com/zeroturnaround/zt-zip获得

它具有标准的打包和解包功能(在流和文件系统上)+许多辅助方法来测试存档中的文件或添加/删除条目。

另一个选项是JZlib。根据我的经验,它不像zip4J那样“以文件为中心”,所以如果您需要处理内存中的blobs而不是文件,那么您可能需要考虑一下它。

完全实现用zip4j压缩/解压缩文件夹/文件


这种依赖性添加到构建管理器。或者,从在这里添加它下载最新的JAR文件到项目构建路径。class波纹管可以压缩和解压缩任何文件或文件夹有或没有密码保护-

import java.io.File;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.util.Zip4jConstants;
import net.lingala.zip4j.core.ZipFile;


public class Compressor {
public static void zip (String targetPath, String destinationFilePath, String password) {
try {
ZipParameters parameters = new ZipParameters();
parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);


if (password.length() > 0) {
parameters.setEncryptFiles(true);
parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_AES);
parameters.setAesKeyStrength(Zip4jConstants.AES_STRENGTH_256);
parameters.setPassword(password);
}
                

ZipFile zipFile = new ZipFile(destinationFilePath);
                

File targetFile = new File(targetPath);
if (targetFile.isFile()) {
zipFile.addFile(targetFile, parameters);
} else if (targetFile.isDirectory()) {
zipFile.addFolder(targetFile, parameters);
} else {
//neither file nor directory
}


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

public static void unzip(String targetZipFilePath, String destinationFolderPath, String password) {
try {
ZipFile zipFile = new ZipFile(targetZipFilePath);
if (zipFile.isEncrypted()) {
zipFile.setPassword(password);
}
zipFile.extractAll(destinationFolderPath);


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

/**/ /// for test
public static void main(String[] args) {
        

String targetPath = "target\\file\\or\\folder\\path";
String zipFilePath = "zip\\file\\Path";
String unzippedFolderPath = "destination\\folder\\path";
String password = "your_password"; // keep it EMPTY<""> for applying no password protection
            

Compressor.zip(targetPath, zipFilePath, password);
Compressor.unzip(zipFilePath, unzippedFolderPath, password);
}/**/
}

有关更详细的用法,请参见在这里