Java解压RAR文件

156 阅读5分钟

1. 依赖

<dependency>
    <groupId>com.github.axet</groupId>
    <artifactId>java-unrar</artifactId>
    <version>1.7.0-8</version>
</dependency>
<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>

2. 相关类

2.1 RarUtil

@Slf4j
public class RarUtil {

    /**
     * 解压rar文件
     * @param rarFile
     * @param dstDirectoryPath
     * @return
     */
    public static void unRar(File rarFile, String dstDirectoryPath){
        if(dstDirectoryPath.startsWith("//")){
            // 在Linux系统下,路径会已//开头需要替换为/
            // 在windows系统下,路径会以会已磁盘符号开后,
            log.info("==》Linux系统中,路径替换之前:{}", dstDirectoryPath);
            dstDirectoryPath = dstDirectoryPath.replace("//","/");
        }
        log.info("最终解压目标目录路径:{}", dstDirectoryPath);

        // 确保目标目录存在
        File dstDir = new File(dstDirectoryPath);
        if (!dstDir.exists()) {
            if (!dstDir.mkdirs()) {
                log.error("无法创建目标目录: {}", dstDirectoryPath);
                return;
            }
        }
        // 以只读模式 ("r") 打开一个RAR文件
        try (RandomAccessFile randomAccessFile = new RandomAccessFile(rarFile, "r");
             // 打开存档文件,null表示自动检测存档类型
             // RandomAccessFileInStream将RandomAccessFile包装成输入流,以便SevenZip库可以读取
             IInArchive archive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile))) {

            // 获取存档中的项目总数
            int numberOfItems = archive.getNumberOfItems();
            int[] indices = new int[numberOfItems];
            for (int i = 0; i < numberOfItems; i++) {
                indices[i] = i;
            }

            // 开始提取文件
            // in: 指定要提取的文件索引数组;
            // false: 表示不测试文件完整性,直接提取
            // 提供一个回调接口实例来处理提取过程中的事件,dstDirectoryPath是目标目录路径。
            archive.extract(indices, false, new RarExtractCallback(archive, dstDirectoryPath));
        } catch (Exception e) {
            log.error("解压文件时发生错误: {}", rarFile.getAbsolutePath(), e);
        }
    }
}

2.2 RarExtractCallback

@Slf4j
public class RarExtractCallback implements IArchiveExtractCallback {

    private IInArchive inArchive;
    private String ourDir;
    private String lastPath = "";


    public RarExtractCallback(IInArchive inArchive, String ourDir) {
        this.inArchive = inArchive;
        this.ourDir = ourDir;
    }

    @Override
    public ISequentialOutStream getStream(int index, ExtractAskMode extractAskMode) throws SevenZipException {
        final String path = (String) inArchive.getProperty(index, PropID.PATH);
        final boolean isFolder = (Boolean) inArchive.getProperty(index, PropID.IS_FOLDER);

        if (isFolder) {
            File folder = new File(ourDir + File.separator + path);
            if (!folder.exists() && !folder.mkdirs()) {
                log.error("无法创建目录: {}", folder.getAbsolutePath());
                return null;
            }
            return null;
        }

        File file = new File(ourDir + File.separator + path);
        boolean append = path.equals(lastPath);
        lastPath = path;

        return data -> {
            try {
                saveFile(file, data, append);
            } catch (Exception e) {
                log.error("保存文件时发生错误: {}", file.getAbsolutePath(), e);
                return 0;
            }
            return data.length;
        };
    }


 
    @Override
    public void prepareOperation(ExtractAskMode extractAskMode) throws SevenZipException {
 
    }
 
    @Override
    public void setOperationResult(ExtractOperationResult extractOperationResult) throws SevenZipException {
 
    }
 
    @Override
    public void setTotal(long l) throws SevenZipException {
 
    }
 
    @Override
    public void setCompleted(long l) throws SevenZipException {
 
    }

    public void saveFile(File file, byte[] msg, boolean append) {
        File parent = file.getParentFile();
        if (!parent.exists() && !parent.mkdirs()) {
            log.error("创建目录异常: {}", parent.getAbsolutePath());
            return ;
        }

        try (FileOutputStream fos = new FileOutputStream(file, append)) {
            fos.write(msg);
            fos.flush();
            log.info("解压文件已成功保存: {}", file.getAbsolutePath());
        } catch (IOException e) {
            log.error("保存文件时发生错误: {}", file.getAbsolutePath(), e);
        }
    }
}

2.3 ExcelDataBO

@Data
public class ExcelDataBO {
    @ExcelProperty("名字")
    private String string;

    @ExcelProperty("日期")
    private Date date;

    @ExcelProperty("工资")
    private Double doubleData;
}

2.4 RarController

@Slf4j
@Api(tags = "Rar解压")
@RestController
@RequiredArgsConstructor
@RequestMapping("/rar")
public class RarController {

    @PostMapping("/unRar")
    @ApiOperation("Rar解压功能")
    public String unRar(@RequestParam("file") MultipartFile file) throws Exception {
        String filename = file.getOriginalFilename();
        if (StrUtil.isBlank(filename) || !filename.toLowerCase().endsWith(".rar")) {
            throw new Exception("【" + filename + "】文件不是rar格式!");
        }

        String currentDirectory = System.getProperty("user.dir") + File.separator + System.currentTimeMillis();
        FileUtil.mkdir(currentDirectory);
        String rarFilePath = currentDirectory + File.separator + "data.rar";

        try {
            // 下载文件到本地
            File localFile = FileUtil.writeBytes(file.getBytes(), rarFilePath);
            log.info("文件已保存到本地: {}", rarFilePath);

            // 解压文件
            RarUtil.unRar(localFile, currentDirectory);

            // 通过文件夹地址获取里面的文件
            File folder = new File(currentDirectory);
            if (folder.exists() && folder.isDirectory()) {
                File[] excelFiles = folder.listFiles((dir, name) -> name.toLowerCase().endsWith(".xlsx") || name.toLowerCase().endsWith(".xls"));
                if (excelFiles != null) {
                    for (File excelFile : excelFiles) {
                        log.info("保存【{}】Excel数据", excelFile);
                        saveExcelData(excelFile);
                    }
                }
            }
            return "操作完成";
        }  catch (Exception e) {
            log.error("文件上传或解压失败: {}", e.getMessage(), e);
            FileUtil.del(currentDirectory);
            throw new Exception("文件上传或解压失败!");
        } finally {
            log.info("删除临时文件夹: {}", currentDirectory);
            FileUtil.del(currentDirectory);
        }
    }

    /**
     * 保存Excel数据
     * @param excelFile
     */
    private static void saveExcelData(File excelFile) {
        EasyExcel.read(excelFile, ExcelDataBO.class, new ReadListener<ExcelDataBO>() {
            /**
             * 单次缓存的数据量
             */
            public static final int BATCH_COUNT = 100;
            /**
             *临时存储
             */
            private List<ExcelDataBO> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

            @Override
            public void invoke(ExcelDataBO data, AnalysisContext context) {
                log.info("解析数据:{}", data);
                cachedDataList.add(data);
                if (cachedDataList.size() >= BATCH_COUNT) {
                    saveData();
                    // 存储完成清理 list
                    cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                saveData();
            }

            /**
             * 加上存储数据库
             */
            private void saveData() {
                log.info("存储数据库成功【{}】条!", cachedDataList.size());
            }
        }).sheet().doRead();
    }

}

3. 日志输入

3.1 window系统

输入日志: 文件已保存到本地: D:\RAR测试数据\1739805914204\data.rar 最终解压目标目录路径:D:\RAR测试数据\1739805914204 解压文件已成功保存: D:\RAR测试数据\1739805914204\测试数据01.xlsx 解压文件已成功保存: D:\RAR测试数据\1739805914204\测试数据02.xlsx 保存【D:\RAR测试数据\1739805914204\测试数据01.xlsx】Excel数据 解析数据:DemoData(string=张1, date=Mon Jan 01 00:00:00 CST 2024, doubleData=22222.2) 解析数据:DemoData(string=张2, date=Tue Jan 02 00:00:00 CST 2024, doubleData=22223.2) 解析数据:DemoData(string=张3, date=Wed Jan 03 00:00:00 CST 2024, doubleData=22224.2) 存储数据库成功【3】条! 保存【D:\RAR测试数据\1739805914204\测试数据02.xlsx】Excel数据 解析数据:DemoData(string=李1, date=Wed Jan 01 00:00:00 CST 2025, doubleData=888.88) 解析数据:DemoData(string=李2, date=Thu Jan 02 00:00:00 CST 2025, doubleData=889.88) 解析数据:DemoData(string=李3, date=Fri Jan 03 00:00:00 CST 2025, doubleData=890.88) 存储数据库成功【3】条! 删除临时文件夹: D:\RAR测试数据\1739805914204

3.2 linux系统

输入日志: 文件已保存到本地: //1739725342943/data.rar ==》Linux系统中,路径替换之前://1739725342943 最终解压目标目录路径:/1739725342943 解压文件已成功保存: /1739725342943/test01.xlsx 解压文件已成功保存: /1739725342943/test02.xlsx 保存【/1739725342943/test01.xlsx】Excel数据 解析数据:TestController.DemoData(string=张1, date=Mon Jan 01 00:00:00 CST 2024, doubleData=22222.2) 解析数据:TestController.DemoData(string=张2, date=Tue Jan 02 00:00:00 CST 2024, doubleData=22223.2) 解析数据:TestController.DemoData(string=张3, date=Wed Jan 03 00:00:00 CST 2024, doubleData=22224.2) 存储数据库成功【3】条! 保存【/1739725342943/test02.xlsx】Excel数据 解析数据:TestController.DemoData(string=李1, date=Wed Jan 01 00:00:00 CST 2025, doubleData=888.88) 解析数据:TestController.DemoData(string=李2, date=Thu Jan 02 00:00:00 CST 2025, doubleData=889.88) 解析数据:TestController.DemoData(string=李3, date=Fri Jan 03 00:00:00 CST 2025, doubleData=890.88) 存储数据库成功【3】条! 删除临时文件夹: //1739725342943