Windows上可靠的File.renameTo()替代方案?

Jon*_*nik 88 java windows file-io file

Java File.renameTo()似乎是有问题的,特别是在Windows上.正如API文档所说,

此方法行为的许多方面本质上依赖于平台:重命名操作可能无法将文件从一个文件系统移动到另一个文件系统,它可能不是原子的,如果具有目标抽象路径名的文件可能不会成功已经存在.应始终检查返回值以确保重命名操作成功.

就我而言,作为升级过程的一部分,我需要移动(重命名)一个可能包含千兆字节数据的目录(许多子目录和不同大小的文件).移动总是在同一个分区/驱动器内完成,因此没有必要实际移动磁盘上的所有文件.

这里不应该有任何文件锁定的目录中的内容被移动了,不过,很多时候,renameTo()无法完成其工作,并返回false.(我只是猜测某些文件锁可能会在Windows上随意过期.)

目前我有一个使用复制和删除的回退方法,但这很糟糕,因为它可能需要长时间,具体取决于文件夹的大小.我还在考虑简单地记录用户可以手动移动文件夹以避免等待数小时的事实.但正确的方式显然是自动而快速的.

所以我的问题是,您是否知道使用普通JDK或某些外部库在Windows上使用Java快速移动/重命名的另一种可靠方法.或者,如果您知道一种简单的方法来检测和释放给定文件夹及其所有内容(可能是数千个单独的文件)的任何文件锁,那也没关系.


编辑:在这种特殊情况下,我们似乎只是renameTo()考虑了一些事情而离开了; 看到这个答案.

小智 47

另请参见Files.move()JDK 7中的方法.

一个例子:

String fileName = "MyFile.txt";

try {
    Files.move(new File(fileName).toPath(), new File(fileName).toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING);
} catch (IOException ex) {
    Logger.getLogger(SomeClass.class.getName()).log(Level.SEVERE, null, ex);
}
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是Java7并不总是答案(就像42一样) (7认同)

Jon*_*nik 25

对于它的价值,还有一些进一步的概念:

  1. 在Windows上,renameTo()如果目标目录存在,则似乎失败,即使它是空的.这让我感到惊讶,就像我在Linux上尝试过的那样,renameTo()如果目标存在,只要它是空的就会成功.

    (显然我不应该假设这种事情在不同的平台上都是一样的;这正是Javadoc所警告的.)

  2. 如果您怀疑可能存在一些挥之不去的文件锁,那么在移动/重命名之前稍等一会可能有所帮助.(在我们的安装程序/升级程序中,我们添加了一个"睡眠"操作和一个不确定的进度条大约10秒,因为某些文件可能会挂起一个服务).也许甚至做一个简单的重试机制尝试renameTo(),然后等待一段时间(可能会逐渐增加),直到操作成功或达到一些超时.

在我的情况下,大多数问题似乎已经通过考虑上述两个问题得到了解决,所以我们不需要做本机内核调用,或者毕竟是一些这样的事情.

  • 6.5年后,我认为是时候接受[JDK 7答案](http://stackoverflow.com/a/5451161/56285),特别是因为很多人认为它有用.=) (4认同)
  • 我现在接受我自己的答案,因为它描述了我们案例中的帮助.尽管如此,如果有人对renameTo()更普遍的问题提出了一个很好的答案,请随意发布,我很乐意重新考虑接受的答案. (2认同)

Myk*_*naC 19

最初的帖子要求"使用普通JDK或某些外部库在Windows上使用Java快速移动/重命名的另一种可靠方法."

此处未提及的另一个选项是apache.commons.io库的v1.3.2或更高版本,其中包括FileUtils.moveFile().

它抛出一个IOException,而不是在出错时返回boolean false.

另见另一个帖子中的大lep的回应.

  • 此外,看起来JDK 1.7将包含更好的文件系统I/O支持.查看java.nio.file.Path.moveTo():http://java.sun.com/javase/7/docs/api/java/nio/file/Path.html (2认同)
  • JDK 1.7没有方法`java.nio.file.Path.moveTo()` (2认同)

Joh*_*dep 5

在 Windows 上,我使用Runtime.getRuntime().exec("cmd \\c ")然后使用命令行重命名功能来实际重命名文件。它更加灵活,例如,如果您想将目录中所有txt文件的扩展名重命名为bak,只需将其写入输出流:

重命名*.txt *.bak

我知道这不是一个好的解决方案,但显然它一直对我有用,比 Java 内联支持好得多。


wup*_*ppi 5

在我的情况下,它似乎是我自己的应用程序中的一个死对象,它保留了该文件的句柄。所以这个解决方案对我有用:

for (int i = 0; i < 20; i++) {
    if (sourceFile.renameTo(backupFile))
        break;
    System.gc();
    Thread.yield();
}
Run Code Online (Sandbox Code Playgroud)

优点:它非常快,因为没有带有特定硬编码时间的 Thread.sleep()。

缺点:20 的限制是一些硬编码的数字。在我所有的测试中, i=1 就足够了。但可以肯定的是,我把它留在了 20 岁。