JAVA实现解压缩RAR5

前言

在实际项目中,我们经常会遇到用户上传压缩包,需要我们的系统解压用户上传的压缩包来实现相应的业务。目前各种操作系统中主流的压缩包有三种: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();
        }
    }

注意事项

  1. 使用前需要确保sevenzipjbinding库已正确初始化
  2. 解压大文件时可能会占用较多内存
  3. 确保有足够的磁盘空间存放解压后的文件
  4. 解压路径需要写权限
  5. 工具类中已包含静态初始化块,会自动加载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.全力研究中…

  • 解压进度回调
  • 压缩文件解密
  • 分卷压缩处理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值