使用Commons IO复制文件时如何锁定文件?

4

我正在使用Apache Commons IO:

     FileUtils.copyFileToDirectory(srcFile, destDir)

如何让Windows在复制过程中锁定目标文件?如果我使用以下方式,Windows可以正确地锁定文件:

      Runtime.getRuntime().exec(
      "cmd /c copy /Y \"" + srcFile.getCanonicalPath() + "\" \""
          + destDir.getCanonicalPath() + "\"").waitFor();

注: 这里的争用并不是发生在本地程序中,而是外部程序。该文件正在被复制到远程系统。远程系统在完成复制之前正在处理该文件。由于系统使用的是Windows,正常的复制会锁定文件并阻止外部程序访问。

3个回答

6
java.nio.channels.FileChannel可以让你使用底层文件系统的方法在文件上获取FileLock,前提是该功能得到支持。
该锁定可跨机器进程使用,即使是非Java进程也可以。 (实际上,该锁定代表特定JVM实例保持,因此不适合管理进程中多个线程或同一JVM中的多个进程之间的争用)。
这里有很多注意事项,但如果您正在处理Windows,则值得研究。
从javadoc中得知:
此文件锁定API旨在直接映射到底层操作系统的本地锁定功能。 因此,应在所有具有对文件的访问权限的程序中看到对文件进行的锁定,而与编写这些程序的语言无关。
锁定是否实际上会防止另一个程序访问锁定区域的内容取决于系统,因此未指定。 一些系统的本机文件锁定设施仅为建议性,这意味着程序必须协作地遵守已知的锁定协议,以保证数据完整性。 在其他系统上,本机文件锁定是强制性的,这意味着如果一个程序锁定了文件的某个区域,则其他程序实际上将被阻止以违反锁定的方式访问该区域。 在其他系统上,本机文件锁定是强制性的,这意味着如果一个程序锁定了文件的某个区域,则其他程序实际上将被阻止以违反锁定的方式访问该区域。 为确保跨平台的一致和正确行为,强烈建议将此API提供的锁视为建议性锁。
在某些系统上,获取文件区域的强制锁定会防止该区域映射到内存中,反之亦然。 结合锁定和映射的程序应准备好此组合失败。
在某些系统上,关闭通道会释放Java虚拟机在底层文件上持有的所有锁定,而不管这些锁定是通过该通道还是通过在同一文件上打开的另一个通道获得的。 在程序中强烈建议使用唯一通道来获取给定文件上的所有锁定。
一些网络文件系统仅允许在锁定区域对齐页面且整个倍数下使用文件锁定进行内存映射的文件。 一些网络文件系统不会在扩展到某个位置的区域上实现文件锁定,通常为230或231。 通常,在锁定驻留在网络文件系统上的文件时应格外小心。

4

Java不原生支持文件锁。

如果争用文件是来自于程序内部,或许你需要在文件复制的基础上建立额外的同步以确保并发写操作不会相互干扰。但是,如果争用是来自于软件之外,则你几乎无能为力。你可以尝试将文件写入临时目录,然后重命名它,因为重命名是比较原子的(取决于文件系统)。

如果你能提供一些关于为什么需要首先锁定文件的更多信息,那会很有帮助。

这里的争用不是本地程序引起的,而是外部因素引起的。该文件正在被复制到远程系统。远程系统在完成复制之前正在处理该文件。由于系统都是Windows,普通复制操作会锁定文件并阻塞外部程序的访问。

在这种情况下,你应该尝试将文件写入临时文件,然后在文件完全复制后将其重命名。文件重命名是原子操作(在非网络文件系统上),所以这对你应该可行。


1
在复制文件时,始终将其复制到临时文件名,并在完成写入后将文件重命名。这样,另一个进程就永远看不到损坏的文件。
更好的做法是,可以写入*.tmp。然后检查目标文件是否存在。如果存在,则将目标文件重命名为*.bak。最后将*.tmp重命名为目标文件名。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接