用Files.delete()删除文件时的奇怪行为

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (438)

请考虑下面的Java类示例(下面是pu.xml):

package test.filedelete;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;

import org.apache.commons.io.IOUtils;

public class Main
{
    public static void main(String[] args) throws IOException
    {
        byte[] bytes = "testtesttesttesttesttesttesttesttesttest".getBytes();
        InputStream is = new ByteArrayInputStream(bytes);

        Path tempFileToBeDeleted = Files.createTempFile("test", "");
        OutputStream os = Files.newOutputStream(tempFileToBeDeleted);
        IOUtils.copy(is, os);

        deleteAndCheck(tempFileToBeDeleted);

        // breakpoint 1
        System.out.println("\nClosing stream\n");

        os.close();

        deleteAndCheck(tempFileToBeDeleted);
    }

    private static void deleteAndCheck(Path file) throws IOException
    {
        System.out.println("Deleting file: " + file);
        try
        {
            Files.delete(file);
        }
        catch (NoSuchFileException e)
        {
            System.out.println("No such file");
        }
        System.out.println("File really deleted: " + !Files.exists(file));

        System.out.println("Recreating deleted file ...");
        try
        {
            Files.createFile(file);
            System.out.println("Recreation successful");
        }
        catch (IOException e)
        {
            System.out.println("Recreation not possible, exception: " + e.getClass().getName());
        }
    }
}

在Windows 7上运行Main方法时,它会产生以下输出:

Deleting file: C:\Users\MSCHAE~1\AppData\Local\Temp\test6100073603559201768
File really deleted: true
Recreating deleted file ...
Recreation not possible, exception: java.nio.file.AccessDeniedException

Closing stream

Deleting file: C:\Users\MSCHAE~1\AppData\Local\Temp\test6100073603559201768
No such file
File really deleted: true
Recreating deleted file ...
Recreation successful
  • 为什么第一次调用Files.DELETE()时不抛出异常?
  • 为什么对Files.ist()的以下调用返回false?
  • 为什么不能重新创建文件?

关于最后一个问题,我注意到当您在断点1停止时,该文件在Explorer中仍然可见。当您终止JVM时,文件无论如何都会被删除。关闭流后,删除AndCheck()将按预期工作。

在我看来,在关闭流之前,删除没有传播到操作系统,FilesAPI没有正确地反映这一点。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>test</groupId>
    <artifactId>filedelete</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>
    </dependencies>
</project>

更新澄清

如果流被关闭并且调用了Files.delete()(最后一个操作触发器),或者如果Files.DELETE()在没有关闭流的情况下被调用,并且JVM被终止,那么该文件将在Windows Explorer中消失。

提问于
用户回答回答于

你能删除打开的文件吗?

打开文件时删除文件的目录条目是完全有效的。在Unix中,这是默认的语义,在打开的所有文件句柄上设置为该文件。

但是,有一个细微的差别:unix。删除文件名称立马,而Windows仅在关闭最后一个句柄时才删除文件名。...。但是,在关闭(已删除的)文件的最后句柄之前,它将阻止您打开同名文件。

去想想..。

然而,在这两个系统中,删除文件并不一定会使文件消失,只要仍然有一个打开的句柄,它仍然占用磁盘上的空间。只有当最后一个打开的句柄关闭时,文件所占用的空间才会释放。

远足:窗户

在Windows上指定标志是必要的,这使大多数人认为Windows无法删除打开的文件,但实际上并非如此。那只是违约行为。

Windows API文档在过程中似乎(故意?)模糊的事实并没有真正改善这种情况:

CreateFile():

允许对文件或设备上的后续打开操作请求删除访问。 否则,其他进程如果请求删除访问,则无法打开文件或设备。 如果未指定此标志,但已打开文件或设备以进行删除访问,则该函数将失败。注意:Delete Access允许删除和重命名操作。

DeleteFile():

DeleteFile函数在关闭时标记要删除的文件。因此,文件删除直到文件的最后句柄关闭后才会发生。随后调用CreateFile打开文件失败_存取_否认。

对没有名称的文件有一个打开句柄是创建未命名临时文件的最典型方法之一:创建一个新文件,打开它,删除该文件。您现在有了一个无法打开的文件句柄。在Unix上,文件名确实消失了,在Windows上,您不能打开同名的文件。

现在的问题是:

是吗?Files.newOutputStream()FILE_SHARE_DELETE****

望着来源,你可以看到shareDelete实际上,默认为true...。重置它的唯一方法是使用非标准的ExtendedOpenOptionNOSHARE_DELETE...

因此,可以删除Java中打开的文件,除非它们被显式锁定。

为什么我不能重新创建删除的文件?

答案隐藏在DeleteFile()上面:文件只标记为删除,文件仍然在那里。在Windows上,您不能使用名称标记为删除的文件,直到该文件为恰如其分已删除,即文件的所有句柄都已关闭。

名称删除和实际文件删除可能混淆,这可能是Windows首先不允许删除默认打开的文件的原因。

为什么Files.exists()回归false****

Files.exists()在Windows的深层在某个时候打开该文件,我们已经知道我们不能在Windows上重新打开一个已删除但仍然打开的文件。...

详细说明:Java代码调用FileSystemProvider.checkAccess())没有参数,这会调用WindowsFileSystemProvider.checkReadAccess()立即尝试打开文件,结果失败。据我所知,这是你打电话时走的路Files.exist()...

还有另一个代码路径调用GetFileAttributeEx()检索文件属性。再次,当您试图检索已删除但尚未删除文件,但实际上,您不能检索文件的文件属性。标记为删除...

我猜,我会说GetFileAttributeEx()打电话GetFileInformationByHandle()在某个时候,它将永远不会到达,因为它一开始就无法获得文件句柄。

所以实际上,在DeleteFile()为了大多数实际目的,这个文件已经没有了。但是,它仍然有一个名称,显示在目录列表中,在原始文件关闭所有句柄之前,您不能打开同名的文件。

这种行为或多或少是一致的,因为使用GetFileAttributes()若要检查文件是否存在,则实际上是一个文件可访问性检查,它被解释为文件存在...FindFirstFile()(Windows资源管理器用于确定文件列表)查找文件人名但却没有告诉你可达性那些名字。

欢迎来到你脑海中的几个奇怪的循环。

用户回答回答于

如果Files.DELETE没有抛出异常,就意味着它删除了该文件。文件删除javadoc说,“在某些操作系统上,当文件打开并被这个Java虚拟机或其他程序使用时,可能无法删除它”。

扫码关注云+社区