如何在windows上用java创建并自动重命名文件?

brvekthn  于 2021-07-13  发布在  Java
关注(0)|答案(2)|浏览(447)

我正在尝试在windows上正确地使用java实现“编写临时文件并重命名”。
如何在java中自动重命名文件,即使dest文件已经存在?建议重命名文件是“原子操作”(不管“原子”实际上意味着什么)。https://stackoverflow.com/a/20570968/65458 建议编写tmp文件和重命名是跨平台的,并确保最终文件不存在或可由其他进程处理。
所以我试着实现这个方法。下面是我尝试的总结。对于实际问题——跳到底。

写入方法

我尝试了各种方法来编写和重命名文件( content 以及 charsetString 以及 Charset 分别):
使用 java.nio.file.Files :

Files.copy(new ByteArrayInputStream(content.getBytes(charset)), tmpFile);
Files.move(tmpFile, finalFile, StandardCopyOption.ATOMIC_MOVE);

使用Guava(14)和 java.io.File :

com.google.common.io.Files.write(content, tmpFile, charset);
tmpFile.renameTo(finalFile);

或者更模糊的方法:

try (OutputStream os = new FileOutputStream(tmpFile);
        Writer writer = new OutputStreamWriter(os, charset)) {
    writer.write(content);
}
Runtime.getRuntime().exec(
        new String[] { "cmd.exe", "/C", "move " + tmpFile + " " + finalFile }).waitFor();

读取方法

现在假设另一个线程(线程因为我在测试中,在现实生活中它可能是另一个进程)正在执行以下代码版本之一:
具有共同功能:

void waitUntilExists() throws InterruptedException {
    while (!java.nio.file.Files.exists(finalFile)) {
        NANOSECONDS.sleep(1);
    }
}

使用 java.nio.file.Files :

waitUntilExists();
return new String(Files.readAllBytes(finalFile), charset);

使用Guava(14):

waitUntilExists();
return new String(com.google.common.io.Files.toByteArray(finalFile.toFile()), charset);

或者更模糊的方法:

waitUntilExists();
StringBuilder sb = new StringBuilder();
try (InputStream is = new FileInputStream(finalFile.toFile())) {
    byte[] buf = new byte[8192];
    int n;
    while ((n = is.read(buf)) > 0) {
        sb.append(new String(buf, 0, n, charset));
    }
}
return sb.toString();

结果

如果我用“读” java.nio.file.Files 一切正常。
如果我在linux上运行这段代码(我知道这超出了问题的范围),一切都正常。
但是,如果我用Guava或 FileInputStream ,则当可能性高于0.5%(0.005)时,测试失败
java.io.filenotfoundexception:进程无法访问该文件,因为另一个进程正在使用该文件
(由于我的Windows不是英文,所以我自己翻译的信息;提到“另一个进程”是有误导性的,因为对于windows来说,即使这是同一个进程,也很正常(我用显式阻塞验证了)

问题

如何在windows上使用java实现create-then-rename,使最终文件以原子方式出现,即不存在或可以读取?
由于我确实可以控制进程而不是提取文件,所以我不能假设正在使用任何特定的读取方法,甚至不能假设它们是在java中。因此,解决方案应该与上面列出的所有读取方法一起工作。

jpfvwuh4

jpfvwuh41#

这似乎就是windows/ntfs的行为方式。
此外,使用旧io和nio的读取之间的行为差异可能是因为它们使用不同的windows API。
维基百科在文件锁定上说
对于在windows中使用文件读/写API的应用程序,字节范围锁由在windows中执行的文件系统强制执行(也称为强制锁)。对于在windows中使用文件MapAPI的应用程序,不强制执行字节范围锁(也称为建议锁)
虽然wikipedia不是windows的文档,但这仍然有一些启示。
)我写这个答案只是为了让其他有同样想法的人不必写这个。正确的答案,参考文档或报告的错误,非常感谢。)

zzwlnbp8

zzwlnbp82#

jdk中的java.io.file.renameto()函数存在错误报告,该函数在已关闭的windows上不是原子函数,无法修复:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4017593. 所以可能没有一个干净的方法来解决你的问题。

相关问题