java - 无法删除文件,因为它已被另一个进程使用,即使 Stream 已关闭
问题描述
我有一个负责序列化/加密和解密/反序列化给定对象的类。
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.charset.StandardCharsets;
public class SerializingUtils {
public static void serialize(User user, String password, String filePath) {
SecretKey key = makeKey(password);
try {
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, key);
SealedObject sealedObject = new SealedObject(user, cipher);
ObjectOutputStream stream = createOutputStream(filePath, cipher);
stream.writeObject(sealedObject);
stream.close();
} catch (Exception exception) {
exception.printStackTrace();
}
}
public static User deserialize(String filePath, String password) throws StreamCorruptedException, Exception {
SecretKey key = makeKey(password);
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.DECRYPT_MODE, key);
ObjectInputStream stream = createInputStream(filePath, cipher);
SealedObject sealedObject = (SealedObject) stream.readObject();
stream.close();
return (User) sealedObject.getObject(cipher);
}
private static SecretKey makeKey(String password) {
return new SecretKeySpec(password.getBytes(StandardCharsets.UTF_8), "Blowfish");
}
private static ObjectOutputStream createOutputStream(String filePath, Cipher cipher) throws IOException {
FileOutputStream fileStream = new FileOutputStream(filePath);
BufferedOutputStream bufferedStream = new BufferedOutputStream(fileStream);
CipherOutputStream cipherStream = new CipherOutputStream(bufferedStream, cipher);
return new ObjectOutputStream(cipherStream);
}
private static ObjectInputStream createInputStream(String filePath, Cipher cipher) throws Exception {
FileInputStream fileStream = new FileInputStream(filePath);
BufferedInputStream bufferedStream = new BufferedInputStream(fileStream);
CipherInputStream cipherStream = new CipherInputStream(bufferedStream, cipher);
return new ObjectInputStream(cipherStream);
}
}
它工作得很好。我尝试为它编写一些测试:
public class SerializingUtilsTest {
private String path;
private User user;
private String password;
@Before
public void setUp() {
path = FileUtils.getAppFolder() + File.separator + "test_file.pman";
user = new User("Josh");
password = "drAkE";
SerializingUtils.serialize(user, password, path);
}
@Test
public void shouldThrowExceptionIfPasswordIsWrong() {
assertThrows(StreamCorruptedException.class, () -> {
SerializingUtils.deserialize(path, "drake");
});
}
@Test
public void shouldReturnAUserWithCorrectName() throws Exception {
User obtainedUser = SerializingUtils.deserialize(path, password);
assertEquals(user.getName(), obtainedUser.getName());
}
}
这几乎是一个集成测试:它调用实际方法并测试用户输入正确/错误密码时会发生什么。测试也运行良好。但是后来我尝试实现一个拆卸方法来在测试完成后删除创建的文件:
@After
public void tearDown() {
Path filePath = Paths.get(path);
try {
Files.delete(filePath);
} catch (IOException exception) {
exception.printStackTrace();
}
}
但是,tear down 方法在被调用时会抛出此异常(2 次):
java.nio.file.FileSystemException: C:\dev\passman\test_file.pman: The file is already being used by another process
at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:92)
at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:108)
at java.base/sun.nio.fs.WindowsFileSystemProvider.implDelete(WindowsFileSystemProvider.java:274)
at java.base/sun.nio.fs.AbstractFileSystemProvider.delete(AbstractFileSystemProvider.java:105)
at java.base/java.nio.file.Files.delete(Files.java:1146)
at com.passman.utils.SerializingUtilsTest.tearDown(SerializingUtilsTest.java:49)
也许FileStream
s 用于构建ObjectOutputStream
并且ObjectInputStream
没有被关闭,即使我呼吁close
他们。我想知道是否bufferedStream
也cipherStream
保持开放。无论如何,这里真正的问题是,我怎样才能确保里面的所有流在createOutputStream()
我createInputStream()
不再需要它们时关闭?我可以在将流作为参数传递给另一种类型的流后立即关闭它,还是它仍然被引用使用?
我的代码的依赖关系,如果有人想测试整个事情:
// User.java
public class User implements Serializable {
private final String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
// FileUtils.java
public class FileUtils {
public static String getAppFolder() {
try {
return new File(".").getCanonicalPath();
} catch (IOException exception) {
return null;
}
}
}
解决方案
我猜如果在打开和关闭之间抛出异常,您的流将保持打开状态。
public static User deserialize(String filePath, String password) throws StreamCorruptedException, Exception {
SecretKey key = makeKey(password);
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.DECRYPT_MODE, key);
// stream opens here
ObjectInputStream stream = createInputStream(filePath, cipher);
// something goes wrong here and exception is thrown
SealedObject sealedObject = (SealedObject) stream.readObject();
// this doesn't happen...
stream.close();
return (User) sealedObject.getObject(cipher);
}
- - - 尝试这个 - - -
try(var stream = createInputStream(filePath, cipher)) {
// read your object and process it. this is a try-with-resources clause so it auto-closes your stream.
} catch (Exception ex) {
// handle exceptions here (or re-throw them)
}
推荐阅读
- php - 即使在正在运行的 php 被中止或强制终止后删除 pid 文件
- c++ - 线程挂起,高 CPU,C++
- makefile - 即使依赖项已事先显式运行,也使依赖项执行多次
- android-studio - Android Studio “无法解析符号 R”,但项目编译并运行
- c++ - C++中向量和矩阵的乘法
- python - 如何测试创建 Celery 任务的代码?
- java - 将容器连接到mysql数据库并访问它
- angular - show navigation only in inner pages and not on login/registration pages
- flutter - 如何实现 Flutter 功能
- gmail-api - 请Gmail API html帖子示例