依赖
rar解压依赖
<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>
工具类代码
import net.sf.sevenzipjbinding.ExtractOperationResult;
import net.sf.sevenzipjbinding.IInArchive;
import net.sf.sevenzipjbinding.SevenZip;
import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
import net.sf.sevenzipjbinding.util.ByteArrayStream;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* @author: CC
* @Desc:
* @create: 2025-06-16 10:18
**/
public class FileUtils {
public enum FileType {
ZIP, RAR
}
/**
* 解压
*
* @param url 文件下载链接
* @return 文件名到文件输入流的映射 eg:{/test1/小红花.sql=略, /test1/test1-1/小红花.txt=略}
* @throws Exception
*/
public static Map<String, InputStream> extractFile(String url) throws Exception {
Objects.requireNonNull(url);
InputStream inputStream = extractUrlToStream(url);
Map<String, InputStream> fileContents = new HashMap<>();
if (url.toUpperCase().endsWith("." + FileType.ZIP.name())) {
extractZipFile(inputStream, fileContents, "");
} else if (url.toUpperCase().endsWith("." + FileType.RAR.name())) {
extractRarFile(inputStream, fileContents, "");
} else {
throw new RuntimeException("extractFile error, url type " + url + " is not supported");
}
return fileContents;
}
/**
* 解压
*
* @param type 压缩文件类型
* @param in 压缩文件file流
* @return 文件名到文件输入流的映射 eg:{/test1/小红花.sql=略, /test1/test1-1/小红花.txt=略}
* @throws Exception
*/
public static Map<String, InputStream> extractFile(FileType type, InputStream in) throws Exception {
Objects.requireNonNull(type);
Objects.requireNonNull(in);
Map<String, InputStream> fileContents = new HashMap<>();
switch (type) {
case ZIP:
extractZipFile(in, fileContents, "");
break;
case RAR:
extractRarFile(in, fileContents, "");
break;
default:
throw new RuntimeException("extractFile error, Type " + type.name() + " is not supported");
}
return fileContents;
}
/**
* 递归解压Zip文件方法
* <p>
* 可能存在问题:仅限windows压缩,第三方压缩(如:7-zip)存在解压 bug:java.lang.IllegalArgumentException: MALFORMED
* 因为java.util.zip下的格式转换有问题 ,jdk中的zip存在字符编码的问题,windows下压缩的默认编码是GBK。
* 参考 https://www.cnblogs.com/NickyYe/p/4450839.html
*
* @param in 压缩文件输入流
* @param fileContents 解压结果映射 eg:{/test1/小红花.sql=略, /test1/test1-1/小红花.txt=略}
* @param basePath 基础路径,用于处理嵌套压缩文件
*/
public static void extractZipFile(InputStream in, Map<String, InputStream> fileContents, String basePath) throws Exception {
Objects.requireNonNull(fileContents);
if (!basePath.isEmpty() && !basePath.startsWith("/")) {
basePath = "/" + basePath;
}
// 读入流:zip文件里含有中文名称的文件,windows 环境下,默认字符集为GBK,ZipFile 默认使用 UTF-8 字符集,当文件名存在中文时,处理时就会报错。创建 ZipFile 时,设置字符集为GBK(JDK 1.7以上)
ZipInputStream zipInputStream = new ZipInputStream(in, Charset.forName("GBK"));
// 遍历每一个文件
ZipEntry zipEntry = zipInputStream.getNextEntry();
while (zipEntry != null) {
String entryPath = zipEntry.getName().replaceAll("\\\\", "/");
String fullPath = basePath.isEmpty() ? "/" + entryPath : basePath + "/" + entryPath;
if (!zipEntry.isDirectory()) { // 只处理文件,不处理目录
System.out.println("extractZipFile ==> " + fullPath);
if (entryPath.toUpperCase().endsWith("." + FileType.ZIP.name())) {
extractZipFile(zipInputStream, fileContents, fullPath);
} else if (entryPath.toUpperCase().endsWith("." + FileType.RAR.name())) {
extractRarFile(zipInputStream, fileContents, fullPath);
} else {
// 普通文件,直接解压到Map
ByteArrayOutputStream outputStream = extractEntryToStream(zipInputStream);
fileContents.put(fullPath, new ByteArrayInputStream(outputStream.toByteArray()));
}
}
zipInputStream.closeEntry();
zipEntry = zipInputStream.getNextEntry();
}
}
/**
* 递归解压Rar文件方法
* <p>
* 参考:https://blog.youkuaiyun.com/weixin_42477023/article/details/129619848
*
* @param rarInputStream 压缩文件输入流
* @param fileContents 解压结果映射 eg:{/test1/小红花.sql=略, /test1/test1-1/小红花.txt=略}
* @param basePath 基础路径,用于处理嵌套压缩文件
*/
public static void extractRarFile(InputStream rarInputStream, Map<String, InputStream> fileContents, String basePath) throws Exception {
Objects.requireNonNull(fileContents);
if (!basePath.isEmpty() && !basePath.startsWith("/")) {
basePath = "/" + basePath;
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 将输入流复制到临时文件,因为 sevenzipjbinding 需要随机访问
copyStream(rarInputStream, baos);
byte[] rarBytes = baos.toByteArray();
ByteArrayStream inStream = new ByteArrayStream(rarBytes, true, Math.max(1024, rarBytes.length));
try (IInArchive inArchive = SevenZip.openInArchive(null, inStream)) {
// 获取简单接口
ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface();
// 遍历所有文件
for (ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
// 忽略目录
if (!item.isFolder()) {
// 获取文件路径
String entryPath = item.getPath().replaceAll("\\\\", "/");
String fullPath = basePath.isEmpty() ? "/" + entryPath : basePath + "/" + entryPath;
System.out.println("extractRarFile ==> " + fullPath);
InputStream subInputStream = extractISimpleInArchiveItemToStream(fullPath, item);
if (entryPath.toUpperCase().endsWith("." + FileType.RAR.name())) {
extractRarFile(subInputStream, fileContents, fullPath);
} else if (entryPath.toUpperCase().endsWith("." + FileType.ZIP.name())) {
extractZipFile(subInputStream, fileContents, fullPath.replace(".zip", ""));
} else {
fileContents.put(fullPath, subInputStream);
}
}
}
}
}
/**
* 将当前URL转为输出流
*
* @param theUrl URL
* @return InputStream
* @throws IOException
*/
public static InputStream extractUrlToStream(String theUrl) throws IOException {
int retryCount = 0;
long backoffMillis = 100; // 初始退避时间,单位毫秒
while (true) {
try {
URL url = new URL(theUrl);
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
// 设置请求方法和超时时间(可选)
urlConn.setRequestMethod("GET");
urlConn.setConnectTimeout(5000); // 连接超时时间,单位毫秒
urlConn.setReadTimeout(5000); // 读取超时时间,单位毫秒
// 尝试获取输入流
return urlConn.getInputStream(); // 成功获取输入流,返回
// urlConn.disconnect(); // 关闭连接(可选,但推荐在不再需要连接时关闭)
} catch (IOException e) {
if (retryCount >= 3 || e instanceof UnknownHostException) {
// 如果是未知主机异常或已达到最大重试次数,则不再重试,直接抛出异常
throw e;
}
// 等待一段时间后重试
try {
System.err.println("extractUrlToStream Operation failed, retrying... (" + e.getMessage() + ")");
Thread.sleep(backoffMillis);
backoffMillis *= 2; // 应用指数退避策略
retryCount++;
} catch (InterruptedException ie) {
Thread.currentThread().interrupt(); // 保持中断状态
throw new RuntimeException("extractUrlToStream Sleep interrupted: " + ie.getMessage());
}
}
}
}
/**
* 将当前ZipEntry解压为输出流
*/
private static ByteArrayOutputStream extractEntryToStream(ZipInputStream zipInputStream) throws IOException {
// 使用ByteArrayOutputStream保存解压后的数据
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int readLen;
while ((readLen = zipInputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, readLen);
}
return outputStream;
}
/**
* 将当前ISimpleInArchiveItem转为输出流
*/
private static InputStream extractISimpleInArchiveItemToStream(String path, ISimpleInArchiveItem item) throws IOException {
// 提取文件内容
ByteArrayOutputStream fileBaos = new ByteArrayOutputStream();
// 解压文件
ExtractOperationResult result = item.extractSlow(data -> {
try {
fileBaos.write(data);
} catch (IOException e) {
return 0; // 出错时返回 0 表示中断
}
return data.length; // 返回处理的数据长度
});
if (result != ExtractOperationResult.OK) {
throw new IOException("提取文件失败: " + path);
}
return new ByteArrayInputStream(fileBaos.toByteArray());
}
/**
* 辅助方法:将输入流复制到输出流
*/
private static void copyStream(InputStream input, OutputStream output) throws IOException {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = input.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
}
}
测试方法
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Map;
/**
* @author: CC
* @Desc:
* @create: 2025-06-13 16:39
**/
public class Test {
/**
* 将输入流保存到指定文件
*/
private static void saveStreamToFile(InputStream in, String filePath) throws IOException {
File file = new File(filePath);
mkdir(file.getParentFile());
try (BufferedOutputStream out = new BufferedOutputStream(Files.newOutputStream(Paths.get(filePath)))) {
byte[] buffer = new byte[1024];
int length;
while ((length = in.read(buffer)) > 0) {
out.write(buffer, 0, length);
}
}
}
/**
* 创建目录
*/
private static void mkdir(File file) {
if (null == file || file.exists()) {
return;
}
// 如果父目录不存在则创建
mkdir(file.getParentFile());
file.mkdir();
}
public static void main(String[] args) throws Exception {
String zipFilePath = "D:/temp/ziptest/zip/test1.rar";
String desDirectory = "D:/temp/ziptest/unzip/test6rar";
Map<String, InputStream> unzip = FileUtils.extractFile(FileUtils.FileType.RAR, Files.newInputStream(Paths.get(zipFilePath)));
for (String filePath : unzip.keySet()) {
saveStreamToFile(unzip.get(filePath), desDirectory + filePath);
}
}
}