Java Windows 上的可靠 File.renameTo() 替代方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1000183/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Reliable File.renameTo() alternative on Windows?
提问by Jonik
Java's File.renameTo()
is problematic, especially on Windows, it seems.
As the API documentationsays,
JavaFile.renameTo()
是有问题的,尤其是在 Windows 上,似乎。正如API 文档所说,
Many aspects of the behavior of this method are inherently platform-dependent: The rename operation might not be able to move a file from one filesystem to another, it might not be atomic, and it might not succeed if a file with the destination abstract pathname already exists. The return value should always be checked to make sure that the rename operation was successful.
此方法的行为的许多方面本质上是平台相关的:重命名操作可能无法将文件从一个文件系统移动到另一个文件系统,它可能不是原子的,并且如果具有目标抽象路径名的文件可能不会成功已经存在。应始终检查返回值以确保重命名操作成功。
In my case, as part of an upgrade procedure, I need to move (rename) a directory that may contain gigabytes of data (lots of subdirectories and files of varying sizes). The move is always done within the same partition/drive, so there's no real need to physically move all the files on disk.
就我而言,作为升级过程的一部分,我需要移动(重命名)一个可能包含数千兆字节数据(大量子目录和大小不一的文件)的目录。移动总是在同一个分区/驱动器内完成,因此没有真正需要物理移动磁盘上的所有文件。
There shouldn'tbe any file locks to the contents of the dir to be moved, but still, quite often, renameTo() fails to do its job and returns false. (I'm just guessing that perhaps some file locks expire somewhat arbitrarily on Windows.)
这里不应该有任何文件锁定的目录中的内容被移动了,不过,很多时候,renameTo()无法完成其工作,并返回false。(我只是猜测,也许某些文件锁在 Windows 上会有些随意地过期。)
Currently I have a fallback method that uses copying & deleting, but this sucks because it may take a lotof time, depending on the size of the folder. I'm also considering simply documenting the fact that the user can move the folder manually to avoid waiting for hours, potentially. But the Right Way would obviously be something automatic and quick.
目前我有一个使用复制和删除的后备方法,但这很糟糕,因为它可能需要很多时间,具体取决于文件夹的大小。我也在考虑简单地记录这样一个事实,即用户可以手动移动文件夹以避免等待数小时。但正确的方法显然是自动和快速的。
So my question is, do you know an alternative, reliable approach to do a quick move/rename with Java on Windows, either with plain JDK or some external library. Or if you know an easyway to detect and release any file locks for a given folder and all of its contents(possibly thousands of individual files), that would be fine too.
所以我的问题是,您知道在 Windows 上使用 Java 进行快速移动/重命名的另一种可靠方法,无论是使用普通 JDK 还是某些外部库。或者,如果您知道一种简单的方法来检测和释放给定文件夹及其所有内容(可能是数千个单独文件)的任何文件锁定,那也很好。
Edit: In this particular case, it seems we got away using just renameTo()
by taking a few more things into account; see this answer.
编辑:在这种特殊情况下,似乎我们只是renameTo()
通过考虑更多的事情而逃脱了使用;看到这个答案。
采纳答案by Alan
See also the Files.move()
method in JDK 7.
另请参阅Files.move()
JDK 7 中的方法。
An example:
一个例子:
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);
}
回答by Anton Gogolev
You may try robocopy. This is not exactly "renaming", but it's very reliable.
你可以试试robocopy。这并不完全是“重命名”,但它非常可靠。
Robocopy is designed for reliable mirroring of directories or directory trees. It has features to ensure all NTFS attributes and properties are copied, and includes additional restart code for network connections subject to disruption.
Robocopy 设计用于目录或目录树的可靠镜像。它具有确保复制所有 NTFS 属性和属性的功能,并包含额外的网络连接重启代码。
回答by Blindy
To move/rename a file you can use this function:
要移动/重命名文件,您可以使用此功能:
BOOL WINAPI MoveFile(
__in LPCTSTR lpExistingFileName,
__in LPCTSTR lpNewFileName
);
It is defined in kernel32.dll.
它在 kernel32.dll 中定义。
回答by Ravi Wallau
I know it sucks, but an alternative is to create a bat script which outputs something simple like "SUCCESS" or "ERROR", invoke it, wait for it to be executed and then check its results.
我知道这很糟糕,但另一种方法是创建一个 bat 脚本,它输出一些简单的东西,比如“成功”或“错误”,调用它,等待它被执行,然后检查它的结果。
Runtime.getRuntime().exec("cmd /c start test.bat");
Runtime.getRuntime().exec("cmd /c start test.bat");
This thread may be interesting. Check also the Process class on how to read the console output of a different process.
这个话题可能很有趣。还要检查 Process 类,了解如何读取不同进程的控制台输出。
回答by Jonik
For what it's worth, some further notions:
对于它的价值,一些进一步的概念:
On Windows,
renameTo()
seems to fail if the target directory exists, even if it's empty. This surprised me, as I had tried on Linux, whererenameTo()
succeeded if target existed, as long as it was empty.(Obviously I shouldn't have assumed this kind of thing works the same across platforms; this is exactly what the Javadoc warns about.)
If you suspect there may be some lingering file locks, waiting a little before the move/rename mighthelp. (In one point in our installer/upgrader we added a "sleep" action and an indeterminate progress bar for some 10 seconds, because there might be a service hanging on to some files). Perhaps even do a simple retry mechanism that tries
renameTo()
, and then waits for a period (which maybe increases gradually), until the operation succeeds or some timeout is reached.
在 Windows 上,
renameTo()
如果目标目录存在,即使它是空的,似乎也会失败。这让我感到惊讶,因为我曾在 Linux 上尝试过,renameTo()
如果目标存在,只要它是空的,就会成功。(显然,我不应该假设这种事情跨平台的工作方式相同;这正是 Javadoc 警告的内容。)
如果您怀疑可能存在一些挥之不去的文件锁定,在移动/重命名之前稍等片刻可能会有所帮助。(在我们的安装程序/升级程序中,我们添加了一个“睡眠”操作和一个大约 10 秒的不确定进度条,因为可能有服务挂在某些文件上)。甚至可以做一个简单的重试机制,尝试
renameTo()
,然后等待一段时间(可能逐渐增加),直到操作成功或达到某个超时。
In my case, most problems seem to have been solved by taking both of the above into account, so we won't need to do a native kernel call, or some such thing, after all.
就我而言,考虑到以上两点,大多数问题似乎都已解决,因此我们根本不需要执行本机内核调用或诸如此类的事情。
回答by MykennaC
The original post requested "an alternative, reliable approach to do a quick move/rename with Java on Windows, either with plain JDK or some external library."
最初的帖子要求“另一种可靠的方法来在 Windows 上使用 Java 进行快速移动/重命名,无论是使用普通 JDK 还是一些外部库。”
Another option not mentioned yet here is v1.3.2 or later of the apache.commons.iolibrary, which includes FileUtils.moveFile().
此处尚未提及的另一个选项是apache.commons.io库的v1.3.2 或更高版本,其中包括FileUtils.moveFile()。
It throws an IOException instead of returning boolean false upon error.
它抛出一个 IOException 而不是在出错时返回 boolean false。
See also big lep's response in this other thread.
另请参阅big lep在此其他主题中的回应。
回答by Kactus
I know this seems a little hacky, but for what I've been needing it for, it seems buffered readers and writers have no issue making the files.
我知道这看起来有点 hacky,但对于我一直需要它的东西,似乎缓冲的读者和作者在制作文件时没有问题。
void renameFiles(String oldName, String newName)
{
String sCurrentLine = "";
try
{
BufferedReader br = new BufferedReader(new FileReader(oldName));
BufferedWriter bw = new BufferedWriter(new FileWriter(newName));
while ((sCurrentLine = br.readLine()) != null)
{
bw.write(sCurrentLine);
bw.newLine();
}
br.close();
bw.close();
File org = new File(oldName);
org.delete();
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
}
Works well for small text files as part of a parser, just make sure oldName and newName are full paths to the file locations.
作为解析器的一部分,适用于小文本文件,只需确保 oldName 和 newName 是文件位置的完整路径。
Cheers Kactus
干杯仙人掌
回答by crazy horse
The following piece of code is NOT an 'alternative' but has reliably worked for me on both Windows and Linux environments:
以下代码不是“替代方案”,但在 Windows 和 Linux 环境中都对我有用:
public static void renameFile(String oldName, String newName) throws IOException {
File srcFile = new File(oldName);
boolean bSucceeded = false;
try {
File destFile = new File(newName);
if (destFile.exists()) {
if (!destFile.delete()) {
throw new IOException(oldName + " was not successfully renamed to " + newName);
}
}
if (!srcFile.renameTo(destFile)) {
throw new IOException(oldName + " was not successfully renamed to " + newName);
} else {
bSucceeded = true;
}
} finally {
if (bSucceeded) {
srcFile.delete();
}
}
}
回答by Johnydep
On windows i use Runtime.getRuntime().exec("cmd \\c ")
and then use commandline rename function to actually rename files. It is much more flexible, e.g if you want to rename extension of all txt files in a dir to bak just write this to output stream:
在 Windows 上,我使用Runtime.getRuntime().exec("cmd \\c ")
然后使用命令行重命名功能来实际重命名文件。它更灵活,例如,如果您想将目录中所有 txt 文件的扩展名重命名为 bak,只需将其写入输出流:
rename *.txt *.bak
重命名 *.txt *.bak
I know it is not a good solution but apparently it has always worked for me, much better then Java inline support.
我知道这不是一个好的解决方案,但显然它一直对我有用,比 Java 内联支持要好得多。
回答by iltaf khalid
File srcFile = new File(origFilename);
File destFile = new File(newFilename);
srcFile.renameTo(destFile);
The above is the simple code. I have tested on windows 7 and works perfectly fine.
以上是简单的代码。我已经在 Windows 7 上测试过并且工作得很好。