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

Java Files.copy deletes file when access denied

我想复制一个文件并替换现有文件。问题是,如果该文件正在被另一个程序使用,则会发生 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();
        }

    }

}

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

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

解决方案是使用 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();
        }

    }

}

我遇到了同样的问题。我之前找不到检查文件可访问性的方法。 (当然我测试了属性和 Files.isWritable 等等,但这没有帮助)。 Files.delete(file) 也以 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();
       }
}