目录
前言
在实际项目中,我们经常会遇到用户上传压缩包,需要我们的系统解压用户上传的压缩包来实现相应的业务。目前各种操作系统中主流的压缩包有三种:ZIP、7Z、RAR。三种压缩包使用的压缩算法都不相同,所以需要不同的解压工具。ZIP在各种操作系统中都原生支持,在Java中也有诸多的原生库去解压它;7Z是开源的,Java也可以很方便的去解压;然而由于RAR是一种专利格式,Java标准库并不直接支持,但我们可以使用第三方库,目前有两种主流的库:junrar和sevenzipjbinding,本文详解后者的使用方法。
junrar
在实际单元测试中,junrar解压RAR5压缩包会报出com.github.junrar.exception.UnsupportedRarV5Exception异常,表示我们尝试解压的是 RAR5 格式的文件(WinRAR 5.0+ 版本创建),而当前使用的 junrar 库不支持这种格式。但是查询阿里中央仓库发现使用的junrar库已经是最新的版本,所以判断这个库还不支持解压RAR5。
sevenzipjbinding
sevenzipjbinding 是一个集成7Z的解决方案,同时也支持 RAR5 格式的解压。在阿里中央仓库查询得知,这个库同时支持Windows、Linux、macOS 等主流操作系统,有良好的跨平台性,推荐使用此库。
一、准备依赖
sevenzipjbinding有多个版本的依赖,不同的版本jar包的大小也不同,其中主要包括all、Windows、Linux、macOS 。本人直接选择all版本,此依赖项是一个聚合包,它捆绑了 Windows、Linux、macOS 等主流操作系统(包括 x86/x86_64 架构)的本地库(.dll/.so/.dylib)。同时下面给出特定操作系统的依赖,可以按照自己需要选择。
推荐方案:全平台支持
<dependency>
<groupId>net.sf.sevenzipjbinding</groupId>
<artifactId>sevenzipjbinding</artifactId>
<version>16.02-2.01</version>
</dependency>
<dependency>
<groupId>net.sf.sevenzipjbinding</groupId>
<artifactId>sevenzipjbinding-all-platforms</artifactId>
<version>16.02-2.01</version>
</dependency>
Windows
<dependency>
<groupId>net.sf.sevenzipjbinding</groupId>
<artifactId>sevenzipjbinding</artifactId>
<version>16.02-2.01</version>
</dependency>
<dependency>
<groupId>net.sf.sevenzipjbinding</groupId>
<artifactId>sevenzipjbinding-windows-x86</artifactId>
<version>16.02-2.01</version>
</dependency>
Linux
<dependency>
<groupId>net.sf.sevenzipjbinding</groupId>
<artifactId>sevenzipjbinding</artifactId>
<version>16.02-2.01</version>
</dependency>
<dependency>
<groupId>net.sf.sevenzipjbinding</groupId>
<artifactId>sevenzipjbinding-all-linux</artifactId>
<version>16.02-2.01</version>
</dependency>
macOS
<dependency>
<groupId>net.sf.sevenzipjbinding</groupId>
<artifactId>sevenzipjbinding</artifactId>
<version>16.02-2.01</version>
</dependency>
<dependency>
<groupId>net.sf.sevenzipjbinding</groupId>
<artifactId>sevenzipjbinding-mac-x86_64</artifactId>
<version>16.02-2.01</version>
</dependency>
二、编写工具类
package util;
import net.sf.sevenzipjbinding.*;
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@Component
public class Rar5Extractor {
static {
try {
// 初始化 sevenzipjbinding 库
SevenZip.initSevenZipFromPlatformJAR();
} catch (SevenZipNativeInitializationException e) {
throw new RuntimeException("Failed to initialize sevenzipjbinding", e);
}
}
public void extractRar5(File rarFile, File outputDir) throws IOException, SevenZipException {
if (!outputDir.exists()) {
outputDir.mkdirs();
}
RandomAccessFile randomAccessFile = null;
IInArchive inArchive = null;
try {
randomAccessFile = new RandomAccessFile(rarFile, "r");
inArchive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile));
ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface();
for (ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
if (!item.isFolder()) {
Path outputPath = Paths.get(outputDir.getAbsolutePath(), item.getPath());
Files.createDirectories(outputPath.getParent());
try (FileOutputStream fileOutputStream = new FileOutputStream(outputPath.toFile())) {
ExtractOperationResult result = item.extractSlow(new ISequentialOutStream() {
@Override
public int write(byte[] data) throws SevenZipException {
try {
fileOutputStream.write(data);
return data.length; // 返回写入的字节数
} catch (IOException e) {
throw new SevenZipException("写入文件失败", e);
}
}
});
if (result != ExtractOperationResult.OK) {
throw new SevenZipException("解压失败: " + result);
}
}
}
}
} finally {
try {
if (inArchive != null) {
inArchive.close();
}
} catch (SevenZipException e) {
// 忽略关闭异常
e.printStackTrace();
}
if (randomAccessFile != null) {
try {
randomAccessFile.close();
} catch (IOException e) {
// 忽略关闭异常
e.printStackTrace();
}
}
}
}
}
三、单元测试
@Test
public void test() {
try {
//extractRar5(压缩文件路径,解压目录)
Rar5Extractor.extractRar5(new File("C:\\Users\\Administrator\\Desktop\\test.rar"), new File("C:\\Users\\Administrator\\Desktop"));
}catch (Exception e){
e.printStackTrace();
}
}
注意事项
- 使用前需要确保sevenzipjbinding库已正确初始化
- 解压大文件时可能会占用较多内存
- 确保有足够的磁盘空间存放解压后的文件
- 解压路径需要写权限
- 工具类中已包含静态初始化块,会自动加载sevenzipjbinding库
扩展
i.异步解压demo
@Service
public class AsyncRarExtractor {
@Autowired
private Rar5Extractor rar5Extractor;
@Async
public CompletableFuture<Void> extractAsync(File rarFile, File outputDir) {
try {
rar5Extractor.extractRar5(rarFile, outputDir);
return CompletableFuture.completedFuture(null);
} catch (Exception e) {
CompletableFuture<Void> future = new CompletableFuture<>();
future.completeExceptionally(e);
return future;
}
}
}
ii.全力研究中…
- 解压进度回调
- 压缩文件解密
- 分卷压缩处理