Reliable File.renameTo() alternative on Windows?

Go To StackoverFlow.com

85

Java's File.renameTo() is problematic, especially on Windows, it seems. As the API documentation says,

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't be 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.)

Currently I have a fallback method that uses copying & deleting, but this sucks because it may take a lot of 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 easy way 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.


Edit: In this particular case, it seems we got away using just renameTo() by taking a few more things into account; see this answer.

2009-06-16 08:29
by Jonik
You could wait/use JDK 7, which has much better file system support - akarnokd 2009-06-16 08:50
@kd304, actually I can't wait or use an early access version, but interesting to know something like that is on the way - Jonik 2009-06-16 10:05


45

See also the Files.move() method in 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);
}
2011-03-27 18:01
by Alan
unfortunately Java7 is not always the answer (like 42 is - wuppi 2013-01-02 14:00
Even on ubuntu, JDK7, we faced this issue when running code on EC2 with EBS storage. File.renameTo failed and so did File.canWrite - saurabheights 2016-09-23 13:03
Mind that this is as unreliable as File#renameTo(). It just gives a more useful error when it fails. The only reasonably reliable way I've found is to copy the file with Files#copy to the new name, and then delete the original using Files#delete (which delete itself may fail too, for the same reason Files#move may fail) - jwenting 2019-02-26 04:55


25

For what it's worth, some further notions:

  1. 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, where renameTo() 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.)

  2. If you suspect there may be some lingering file locks, waiting a little before the move/rename might help. (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.

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.

2009-06-17 11:38
by Jonik
I'm accepting my own answer for now, as it describes what helped in our case. Still, if someone comes up with a great answer to the more general issue with renameTo(), feel free to post and I'll be happy to reconsider the accepted answer - Jonik 2009-08-23 17:13
Thanks, the first point saved my day! - janenz00 2014-01-24 12:39
6.5 years later, I think it's time to accept the JDK 7 answer instead, especially as so many people consider it helpful. = - Jonik 2016-01-10 13:24


19

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."

Another option not mentioned yet here is v1.3.2 or later of the apache.commons.io library, which includes FileUtils.moveFile().

It throws an IOException instead of returning boolean false upon error.

See also big lep's response in this other thread.

2010-05-28 18:02
by MykennaC
Also, it looks like JDK 1.7 will include better filesystem I/O support. Check out java.nio.file.Path.moveTo(): http://java.sun.com/javase/7/docs/api/java/nio/file/Path.htm - MykennaC 2010-05-28 18:36
JDK 1.7 has no method java.nio.file.Path.moveTo()Malte Schwerhoff 2015-02-20 12:34


4

The following piece of code is NOT an 'alternative' but has reliably worked for me on both Windows and Linux environments:

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();
          }
    }
}
2011-02-17 00:15
by crazy horse
Hmm, this code deletes srcFile even if renameTo (or destFile.delete) fails and the method throws IOException; I'm not sure if that's a good idea - Jonik 2011-02-17 14:27
@Jonik, Thanx, fixed code to not delete src file if renaming fails - crazy horse 2011-04-07 17:16
Thanks for sharing this fixed my rename issue on windows - BillMan 2011-09-14 15:44


4

In my case it seemed to be a dead object within my own application, which kept a handle to that file. So that solution worked for me:

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

Advantage: it is pretty quick, as there is no Thread.sleep() with a specific hardcoded time.

Disadvantage: that limit of 20 is some hardcoded number. In all my tests, i=1 is enough. But to be sure I left it at 20.

2013-01-02 13:59
by wuppi
I did a similar thing, but with a 100ms sleep in the loop - Lawrence Dol 2014-08-10 02:13


3

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.

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.

Cheers Kactus

2010-11-30 03:06
by Kactus
Does this work with binary files too - Mordechai 2017-08-10 04:50


2

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:

rename *.txt *.bak

I know it is not a good solution but apparently it has always worked for me, much better then Java inline support.

2012-01-11 15:40
by Johnydep
Super, this is much better! Thanks! :- - gaffcz 2013-03-07 11:57


1

Why not....

import com.sun.jna.Native;
import com.sun.jna.Library;

public class RenamerByJna {
    /* Requires jna.jar to be in your path */

    public interface Kernel32 extends Library {
        public boolean MoveFileA(String existingFileName, String newFileName);
    }

    public static void main(String[] args) {
        String path = "C:/yourchosenpath/";
        String existingFileName = path + "test.txt";
        String newFileName = path + "renamed.txt";

        Kernel32 kernel32 = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);
            kernel32.MoveFileA(existingFileName, newFileName);
        }
}

works on nwindows 7, does nothing if existingFile does not exist, but obviously could be better instrumented to fix this.

2012-10-25 08:24
by casgage


1

In my case, the error was in the path of the parent directory. Maybe a bug, I had to use the substring to get a correct path.

        try {
            String n = f.getAbsolutePath();
            **n = n.substring(0, n.lastIndexOf("\\"));**
            File dest = new File(**n**, newName);
            f.renameTo(dest);
        } catch (Exception ex) {
           ...
2012-11-16 23:41
by Marcus Becker


1

I had a similar issue. File was copied rather moving on Windows but worked well on Linux. I fixed the issue by closing the opened fileInputStream before calling renameTo(). Tested on Windows XP.

fis = new FileInputStream(originalFile);
..
..
..
fis.close();// <<<---- Fixed by adding this
originalFile.renameTo(newDesitnationForOriginalFile);
2014-04-02 06:20
by Tharaka Manawardhana


0

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.

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.

2009-06-16 19:20
by Ravi Wallau


-2

You may try robocopy. This is not exactly "renaming", but it's very reliable.

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.

2009-06-16 08:34
by Anton Gogolev
Thanks. But since robocopy is not a Java library, it probably wouldn't be very easy to (bundle it and) use it from my Java code.. - Jonik 2009-06-16 08:38


-2

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.

2009-06-16 08:58
by Blindy
I feel going through the trouble of wrapping this in JNI is greater than the effort required to wrap up robocopy in a Process decorator - Kevin Montrose 2009-06-16 09:01
Just offering alternatives - Blindy 2009-06-16 09:21
yep, this is the price you pay for abstraction - and when it leaks, it leaks good = - Chii 2009-06-16 10:31
Thanks, I might consider this if it doesn't get too complicated. I haven't ever used JNI, and couldn't find good examples of calling a Windows kernel function on SO, so I posted this question: http://stackoverflow.com/questions/1000723/what-is-the-easiest-way-to-call-a-windows-kernel-function-from-jav - Jonik 2009-06-16 10:48
You could try a generic JNI wrapper like http://johannburkard.de/software/nativecall/ as this is a pretty simple function call - Peter Smith 2009-07-28 10:38


-8

 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.

2012-04-07 13:58
by iltaf khalid
There are cases where renameTo() does not work reliably; that's the whole point of the question - Jonik 2012-04-13 03:50