2

I would like to copy a file and replace the existing file. The problem is that if the file is in use by another program, an AccessDeniedException occurs, and the existing file is deleted. I would like the existing file to be retained if the copy fails. The following code demonstrates the problem. (Note this problem was also reported in Java Files.copy replace existing deletes file entirely but the OP did not provide a way to reproduce the problem.)

public void copyFile(){

    Path workingCopy = null;

    //Create a plain text file called example-file-error.txt, add some text to the file, and save it in user.home
    Path path = Paths.get(System.getProperty("user.home"), "example-file-error.txt");

    try{
        workingCopy = Files.createTempFile(path.getParent(), "temp", ".txt");

        //Create a locked file, but the lock is actually created by a separate program
        FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ);
        FileLock lock = fileChannel.lock(0, Long.MAX_VALUE, true);

        Files.copy(workingCopy, path, StandardCopyOption.REPLACE_EXISTING);

    }catch(Exception ex){
        ex.printStackTrace();
    }finally{
        try{
            Files.deleteIfExists(workingCopy);
        }catch(IOException ex){
            ex.printStackTrace();
        }

    }

}

Is there a way to retain the original file if the copy fails? Or, is there a way to wait for access to a file before trying to make a copy?

Community
  • 1
  • 1
meyerjp3
  • 197
  • 1
  • 3
  • 16

3 Answers3

1

The solution is to use Files.move() instead of Files.copy() with the options StandardCopyOption.REPLACE_EXISTING and StandardCopyOption.ATOMIC_MOVE. The atomic move prevents the target file from being deleted when an exception occurs. The solution works on Windows 10, but the atomic move is not guaranteed to work on all operating systems. I'm not sure which ones don't allow this option. Solution is below.

public void copyFile(){

    Path workingCopy = null;

    //Create a plain text file called example-file-error.txt, add some text to the file, and save it in user.home
    Path path = Paths.get(System.getProperty("user.home"), "example-file-error.txt");

    try{
        workingCopy = Files.createTempFile(path.getParent(), "temp", ".txt");

        //Create a locked file, but the lock is actually created by a separate program
        FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ);
        FileLock lock = fileChannel.lock(0, Long.MAX_VALUE, true);

        //This line seems to be the solution to the problem
        Files.move(workingCopy, path, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);

    }catch(IOException ex){
        ex.printStackTrace();
    }finally{
        try{
            Files.deleteIfExists(workingCopy);
        }catch(IOException ex){
            ex.printStackTrace();
        }

    }

}
meyerjp3
  • 197
  • 1
  • 3
  • 16
0

I had the same problem. I could not find a way to check the accessibility of the file before. (Sure i tested for the Attributes and Files.isWritable and so on, but this does not help). Also Files.delete(file) ends in AccessDeniedException, but the file is deleted on windows file system. But also a Files.copy() afterwards throws AccessDeniedException.

Finally i handled the problem by using another API (File Channel, from How to copy file from one location to another location?) to copy files.

private static void copyFileUsingChannel(File source, File dest) throws IOException {
    FileChannel sourceChannel = null;
    FileChannel destChannel = null;
    try {
        sourceChannel = new FileInputStream(source).getChannel();
        destChannel = new FileOutputStream(dest).getChannel();
        destChannel.transferFrom(sourceChannel, 0, sourceChannel.size());
       }finally{
           sourceChannel.close();
           destChannel.close();
       }
} 
Ruwen
  • 3,008
  • 1
  • 19
  • 16
-1

Remove this line Files.deleteIfExists(workingCopy); from the finally and put it in the end of the try clause

public void copyFile() {
Path workingCopy = null; //Create a plain text file called example-file-error.txt, add some text to the file, and save it in user.home 
Path path = Paths.get(System.getProperty("user.home"), "example-file-error.txt");
try{ 
 workingCopy = Files.createTempFile(path.getParent(), "temp", ".txt"); //Create a locked file, but the lock is actually created by a separate program 
 FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ);
 FileLock lock = fileChannel.lock(0, Long.MAX_VALUE, true);
 Files.copy(workingCopy, path, StandardCopyOption.REPLACE_EXISTING);
 Files.deleteIfExists(workingCopy);

  }catch(Exception ex)  
{ ex.printStackTrace(); }
omer727
  • 7,117
  • 6
  • 26
  • 39
  • That does not solve the problem. The existing file still gets deleted, but the temporary file is retained. – meyerjp3 Dec 05 '15 at 16:46