
使用 SevenZipJBinding 的 openInArchive 时,若仅调用 archive.close() 而未显式关闭底层 RandomAccessFile,会导致文件句柄未释放,Windows 下无法删除该文件;需手动关闭 RandomAccessFile 实例。
使用 sevenzipjbinding 的 `openinarchive` 时,若仅调用 `archive.close()` 而未显式关闭底层 `randomaccessfile`,会导致文件句柄未释放,windows 下无法删除该文件;需手动关闭 `randomaccessfile` 实例。
在基于 Java 的归档处理中,尤其是使用 sevenzipjbinding 解析 RAR(含 RAR5)等格式时,一个常见却容易被忽视的问题是:文件无法删除,抛出 java.nio.file.FileSystemException: The process cannot access the file because it is being used by another process。根本原因并非 SevenZip 本身存在 Bug,而是其 openInArchive 方法底层依赖 RandomAccessFile 构建输入流(通过 RandomAccessFileInStream),而 IInArchive.close() 仅释放归档内部资源,并不自动关闭传入的 RandomAccessFile。
因此,即使你已调用 archive.close(),只要 RandomAccessFile 实例仍处于打开状态(未调用 close()),操作系统(特别是 Windows)就会锁定该文件,导致后续 Files.delete() 或 file.delete() 失败。
✅ 正确做法是:显式管理 RandomAccessFile 的生命周期——在获取 IInArchive 前创建,在 archive.close() 后立即调用 randomAccessFile.close()。
以下是修复后的完整逻辑示例(关键修改已加注释):
@PostMapping("/upload")
public void extractRar(@RequestBody UploadDto dto) {
byte[] decodedBytes = Base64.getDecoder().decode(dto.data());
String filePath = dto.name();
Path tempPath = Path.of(filePath);
try {
// ✅ 1. 写入临时文件
Files.write(tempPath, decodedBytes);
log.info("Temporary file saved: {}", filePath);
File file = tempPath.toFile();
IInArchive archive = null;
RandomAccessFile randomAccessFile = null;
try {
// ✅ 2. 显式创建 RandomAccessFile(只读)
randomAccessFile = new RandomAccessFile(file, "r");
// ✅ 3. 使用它构建流并打开归档
archive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile));
log.info("Archive contains {} items", archive.getNumberOfItems());
// ? 此处可添加实际解压逻辑(如遍历 item、提取内容到内存/磁盘等)
// 示例:archive.extract(...)
} catch (SevenZipException | FileNotFoundException e) {
throw new RuntimeException("Failed to open archive", e);
} finally {
// ✅ 4. 确保归档和文件流均被关闭(即使发生异常)
if (archive != null) {
try {
archive.close();
} catch (SevenZipException e) {
log.warn("Error closing SevenZip archive", e);
}
}
if (randomAccessFile != null) {
try {
randomAccessFile.close(); // ? 关键:必须显式关闭!
} catch (IOException e) {
log.warn("Error closing RandomAccessFile", e);
}
}
}
// ✅ 5. 此时文件句柄已完全释放,可安全删除
Files.delete(tempPath);
log.info("Temporary file deleted successfully");
} catch (IOException e) {
throw new RuntimeException("Failed during RAR processing", e);
}
}? 重要注意事项:
-
不要省略
finally块中的双关闭逻辑:archive.close()和randomAccessFile.close()缺一不可,且应独立捕获各自异常,避免一个失败阻断另一个关闭。 -
避免资源泄漏:若解压逻辑较长或可能抛异常,务必用
try-finally或try-with-resources(需封装适配器)确保关闭。 -
替代方案(进阶):若希望彻底避免落盘临时文件,可考虑将 base64 数据先加载至
ByteArrayInputStream,再借助SevenZip.openInArchive(null, new SeekableInStreamAdapter(...))——但sevenzipjbinding原生不支持纯内存流,需自行实现SeekableInStream接口(如基于ByteBuffer的SeekableInMemoryStream),复杂度较高,适用于高并发/敏感场景。 -
Spring Boot 环境建议:生产环境应使用
Files.createTempFile()生成唯一临时路径,而非直接使用客户端传入的dto.name(),防止路径遍历或覆盖风险。
总结:SevenZipJBinding 的资源管理是“半托管”模式——归档对象由库管理,但底层文件流需开发者负责。牢记 RandomAccessFile 必须手动关闭,这是解决 Windows 下文件删除失败的最直接、最可靠方式。

















