访问被拒绝时,Java Files.copy会删除文件

时间:2015-12-05 16:24:11

标签: java nio

我想复制一个文件并替换现有文件。问题是如果该文件正被另一个程序使用,则会发生AccessDeniedException,并删除现有文件。如果复制失败,我希望保留现有文件。以下代码演示了此问题。 (注意Java Files.copy replace existing deletes file entirely中也报告了这个问题,但OP没有提供重现问题的方法。)

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();
        }

    }

}

如果复制失败,有没有办法保留原始文件?或者,有没有办法在尝试复制之前等待访问文件?

3 个答案:

答案 0 :(得分:0)

从finally中删除此行Files.deleteIfExists(workingCopy);并将其放在try子句的末尾

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(); }

答案 1 :(得分:0)

解决方案是使用Files.move()而不是Files.copy()以及选项StandardCopyOption.REPLACE_EXISTING和StandardCopyOption.ATOMIC_MOVE。原子移动可防止在发生异常时删除目标文件。该解决方案适用于Windows 10,但不保证原子移动适用于所有操作系统。我不确定哪些人不允许这个选项。解决方案如下。

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();
        }

    }

}

答案 2 :(得分:0)

我遇到了同样的问题。我以前找不到检查文件可访问性的方法。 (当然我测试了Attributes和Files.isWritable等等,但这没有帮助)。 Files.delete(文件)也以AccessDeniedException结束,但该文件在Windows文件系统上被删除。但是之后的Files.copy()也会抛出AccessDeniedException。

最后,我通过使用另一个API(来自How to copy file from one location to another location?的文件频道)来处理问题。

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();
       }
}