加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > Windows > 正文

如何在Windows上用Java创建 – 然后 – 原子重命名文件?

发布时间:2020-12-14 04:22:51 所属栏目:Windows 来源:网络整理
导读:我正在尝试在 Windows上使用 Java正确实现“写临时文件并重命名”. How to atomically rename a file in Java,even if the dest file already exists?建议重命名文件是“原子操作”(无论“原子”实际上是什么意思). https://stackoverflow.com/a/20570968/65
我正在尝试在 Windows上使用 Java正确实现“写临时文件并重命名”.

How to atomically rename a file in Java,even if the dest file already exists?建议重命名文件是“原子操作”(无论“原子”实际上是什么意思).
https://stackoverflow.com/a/20570968/65458建议编写tmp文件并重命名是跨平台的,并确保最终文件不存在或可由其他进程处理.

所以我试着实际实现这种方法.以下是我的尝试摘要.对于实际问题 – 跳到底部.

写方法

我尝试了各种编写和重命名文件的方法(内容和字符集分别是String和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);

使用番石榴(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,n,charset));
    }
}
return sb.toString();

结果

如果我使用“java.nio.file.Files方法”阅读,一切正常.

如果我在Linux上运行此代码(超出此问题的范围,我知道),一切正常.

但是,如果我使用Guava或FileInputStream实现读取,那么可能性高于0.5%(0.005),测试失败

java.io.FileNotFoundException: Process cannot access the file,because it is being used by another process

(由我自己翻译的消息导致我的窗口不是英文;提到“另一个进程”是误导性的,因为即使这是相同的过程,Windows也是正常的,我用明确的阻止验证了.)

如何在Windows上使用Java实现create-then-rename,以便最终文件以原子方式显示,即要么不存在,要么可以读取?

因为我对进程的控制比拾取文件所控制的,我不能假设使用任何特定的读取方法,或者甚至它们都是Java.因此,该解决方案应适用于上面列出的所有读取方法.

这似乎只是Windows / NTFS的行为方式.

此外,使用旧IO和NIO的读取之间的行为差??异可能是因为它们使用不同的Windows API.

Wikipedia on File locking说

For applications that use the file read/write APIs in Windows,
byte-range locks are enforced (also referred to as mandatory locks) by
the file systems that execute within Windows. For applications that
use the file mapping APIs in Windows,byte-range locks are not
enforced (also referred to as advisory locks.)

虽然维基百科不是Windows的文档,但这仍然有所启发.

(我只是把这个答案放在一起,以便其他人也不必写这个.真正的答案,参考文档或报告的错误,非常感谢.)

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读