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

请考虑以下示例Java类(下面的pom.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());

}

}

}

我写一个FileOutputStream,然后尝试删除该文件, 而不先关闭Stream 。这是我最初的问题,当然是错误的,但它导致了一些奇怪的发现。

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

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.exist()的调用返回false?
  • 为什么无法重新创建文件?

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

在我看来,删除操作不会在关闭流之前传播到操作系统,并且Files API无法正确反映这一点。

有人可以确切说明这里发生了什么吗?

<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资源管理器中消失。

回答:

打开文件时删除文件的目录条目是完全有效的。在Unix中,这是默认的语义,并且Windows

FILE_SHARE_DELETE在打开该文件的所有文件句柄上进行设置时的行为类似。

[编辑:感谢@couling的讨论和更正]

但是,两者之间存在细微的差别:Unix ,而Windows

。但是,它会阻止您打开具有相同名称的文件,直到关闭(已删除)文件的最后一个句柄为止。

去搞清楚 …

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

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

CreateFile()

在文件或设备上启用后续打开操作以请求删除访问。

否则,如果其他进程请求删除访问,则它们将无法打开文件或设备。

如果未指定此标志,但是已打开文件或设备以进行删除访问,则该功能将失败。注意删除访问权限允许删除和重命名操作。

DeleteFile()

DeleteFile函数在关闭时将文件标记为要删除。因此,在关闭文件的最后一个句柄之前,不会发生文件删除。随后调用CreateFile打开文件失败,并显示ERROR_ACCESS_DENIED。

打开没有名称的文件的句柄是创建未命名临时文件的最典型方法之一:创建一个新文件,打开它,然后删除该文件。现在,您可以处理其他人无法打开的文件。在Unix上,文件名确实消失了,在Windows上,您无法打开具有相同名称的文件。

现在的问题是:

查看源代码,您可以看到shareDelete确实默认为true。重置它的唯一方法是使用非标准ExtendedOpenOption

NOSHARE_DELETE

因此,是的,您可以删除Java中打开的文件,除非它们被明确锁定。

DeleteFile()上面的文档中隐藏了答案:该文件仅标记为删除,该文件仍然存在。在Windows上,只有 正确

删除文件(即关闭文件的所有句柄)后,才能使用标记为删除的文件 创建文件。 __

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

Files.exists()在Windows的最深层中

详细来说:Java代码调用FileSystemProvider.checkAccess())不带参数,该调用WindowsFileSystemProvider.checkReadAccess()立即尝试打开文件,因此失败。据我所知,这是您致电时采取的路径Files.exist()

还有另一个代码路径调用GetFileAttributeEx()来检索文件属性。再次没有记载,当您尝试检索

已删除但尚未删除的 文件的属性时会发生什么,但是实际上,您无法检索 标记为删除 的文件的文件属性。

猜猜,我会说在某个时候GetFileAttributeEx()调用GetFileInformationByHandle(),它将永远不会到达,因为它首先无法获得文件句柄。

确实如此,在DeleteFile()文件出于大多数实际目的而消失之后。它仍然有一个名称,但是会显示在目录列表中,并且在原始文件的所有句柄都关闭之前,您无法打开具有相同名称的文件。

这种行为或多或少是一致的,因为GetFileAttributes()用于检查文件是否存在实际上是 文件可访问性 检查,它被解释为 文件存在

FindFirstFile()(由Windows资源管理器用于确定文件列表)查找文件

名, 但不告诉您有关文件 名的可访问 性。

欢迎您再来几个怪异的循环。

以上是 使用Files.delete()删除文件时的奇怪行为 的全部内容, 来源链接: utcz.com/qa/432667.html

回到顶部