You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java调用SevenZip.openArchive后无法删除文件的问题求助

问题:Spring Boot中使用SevenZip解压后无法删除本地文件

问题现象

接收Base64格式的RAR文件内容,使用SevenZip库解压过程正常,但执行归档打开代码后无法删除本地文件,抛出异常:

java.nio.file.FileSystemException: myFile.RAR: The process cannot access the file because it is being used by another process

已尝试Files.delete()和File.delete()两种删除方式均无效,且未找到SevenZip直接通过字节数组或InputStream打开归档的方法。优先使用SevenZip而非junrar(支持RAR5,损坏文件误判更少),希望实现解压后删除临时文件或无需保存本地直接解压,操作系统为Windows 10。


依赖配置(pom.xml)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>demo</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>2.0.1.Final</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>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

DTO类

public record UploadDto(
        @NotNull
        String name,
        @NotNull
        String data) {
}

控制器代码

import net.sf.sevenzipjbinding.IInArchive;
import net.sf.sevenzipjbinding.SevenZip;
import net.sf.sevenzipjbinding.SevenZipException;
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;

@RestController
public class TestController {
    private final Logger log = LoggerFactory.getLogger(TestController.class);

    @PostMapping("/upload")
    public void extractRar(@RequestBody UploadDto dto){
        byte[] decodedBytes = Base64.getDecoder().decode(dto.data());
        try {
            String filePath = dto.name();
            Files.write(Paths.get(filePath), decodedBytes);
            log.info("File saved successfully to: " + filePath);
            File file = new File(filePath);
            IInArchive archive;
            try {
                archive = SevenZip.openInArchive(null, new RandomAccessFileInStream(
                        new RandomAccessFile(file, "r")));
                log.info(String.valueOf(archive.getNumberOfItems()));
            } catch (SevenZipException | FileNotFoundException e) {
                throw new RuntimeException(e);
            }
            archive.close();

            // 1 method
            Files.delete(Path.of(filePath));

            // 2 method
            if (file.delete()) {
                log.info("file deleted successfully");
            } else {
                log.error("failed to delete the temporary file used for unzipping");
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}

请求体示例

{
  "name": "myFile.RAR",
  "data": "UmFyIRoHAM+QcwAADQAAAAAAAABTL3QgkC8AFAAAABQAAAACTeXQiWmKL1cdMAoAIAAAAG15RmlsZS50eHQAsAA/PXJhciBkYXRhIG1vY2sgaGVyZQ0KxD17AEAHAA=="
}

解决方案

原因分析

问题出在RandomAccessFile资源未被正确关闭——虽然调用了archive.close(),但RandomAccessFileInStream包装的底层RandomAccessFile对象没有被显式关闭,导致Windows系统中文件句柄被占用,无法删除文件。

解决方法1:显式关闭RandomAccessFile

将RandomAccessFile单独实例化,在finally块中显式关闭所有资源:

@PostMapping("/upload")
public void extractRar(@RequestBody UploadDto dto){
    byte[] decodedBytes = Base64.getDecoder().decode(dto.data());
    try {
        String filePath = dto.name();
        Files.write(Paths.get(filePath), decodedBytes);
        log.info("File saved successfully to: " + filePath);
        File file = new File(filePath);
        IInArchive archive = null;
        RandomAccessFile raf = null;
        try {
            raf = new RandomAccessFile(file, "r");
            archive = SevenZip.openInArchive(null, new RandomAccessFileInStream(raf));
            log.info(String.valueOf(archive.getNumberOfItems()));
            // 添加解压逻辑
        } catch (SevenZipException | FileNotFoundException e) {
            throw new RuntimeException(e);
        } finally {
            // 先关闭归档,再关闭底层RandomAccessFile
            if (archive != null) {
                try {
                    archive.close();
                } catch (SevenZipException e) {
                    log.error("Failed to close archive", e);
                }
            }
            if (raf != null) {
                try {
                    raf.close();
                } catch (IOException e) {
                    log.error("Failed to close RandomAccessFile", e);
                }
            }
        }

        // 删除文件
        if (Files.deleteIfExists(Path.of(filePath))) {
            log.info("file deleted successfully");
        } else {
            log.error("failed to delete the temporary file used for unzipping");
        }

    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

解决方法2:无需保存本地文件,直接用字节数组解压

使用SevenZipJBinding提供的ByteArrayInStream直接读取Base64解码后的字节数组,跳过本地文件存储步骤:

import net.sf.sevenzipjbinding.impl.ByteArrayInStream;

@PostMapping("/upload")
public void extractRar(@RequestBody UploadDto dto){
    byte[] decodedBytes = Base64.getDecoder().decode(dto.data());
    IInArchive archive = null;
    try {
        // 直接用字节数组打开归档,无需本地文件
        archive = SevenZip.openInArchive(null, new ByteArrayInStream(decodedBytes));
        log.info(String.valueOf(archive.getNumberOfItems()));
        
        // 遍历归档项并提取内容的示例逻辑
        for (int i = 0; i < archive.getNumberOfItems(); i++) {
            if (!archive.isFolder(i)) {
                // 提取文件内容到输入流
                archive.extractSlow(i, data -> {
                    // 处理文件字节数据,比如写入输出流或数据库
                    return data.length;
                });
            }
        }

    } catch (SevenZipException e) {
        throw new RuntimeException(e);
    } finally {
        if (archive != null) {
            try {
                archive.close();
            } catch (SevenZipException e) {
                log.error("Failed to close archive", e);
            }
        }
    }
}

关键说明

  • ByteArrayInStream是SevenZipJBinding专门处理字节数组的输入流实现,完美适配openInArchive方法,彻底避免本地文件的创建与删除问题。
  • 所有资源必须在finally块中显式关闭,防止Windows系统中出现文件句柄泄漏。

内容的提问来源于stack exchange,提问作者S H

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 02:17:05