如何正确打开文件以删除?

10

我原以为这是一个非常简单的任务,但它让我头痛不已。我想打开一个文件以确保我获得了排他性访问权限,测试某些条件,然后将其删除。

现在我正在使用一种99%的方法:

FileStream s = null;
try {
    s = new FileStream (
        path,
        FileMode.Open,
        FileAccess.ReadWrite,
        FileShare.None);
    // some stuff about the file is checked here
    s.Dispose ();
    // hope the file is not accessed by someone else...
    File.Delete (path);
    return true;
}
catch (IOException) {
    if (s !=null) s.Dispose ();
    return false;
}

通常这样做是可行的,但我认为应该有更好的方法来避免边缘情况。

使用DeleteOnClose标志打开文件并不起作用,因为该检查(在已经设置删除标志后打开)可能会指示不应删除该文件。


不确定这是否有帮助,但您是否看过 CodePlex 上的 transactional file manager 项目? - Oded
3
你是否尝试使用FileShare.Delete打开文件,并在释放流之前调用File.Delete? - Fox32
实际上问题出在哪里?如果文件已被锁定(或被其他人删除),第一个打开可能会抛出IOException,就像File.Delete一样。您捕获并处理异常,那么这对您造成了什么问题? - Eddy
我不确定,但如果您不允许在fileShare中写入,其他应用程序就不应该能够写入,而只能删除文件。您可以尝试在关闭流并在资源管理器中删除文件之前添加延迟来验证它。 - Fox32
@Fox32:如果我没记错的话,Windows只区分只读和允许一切。写入、读写、删除等操作都映射到完全访问权限。 - mafu
显示剩余10条评论
3个回答

7

像这样的东西:

    using (FileStream file = new FileStream(path,
       FileMode.Open,
       FileAccess.ReadWrite,
       FileShare.Delete))
    {
        // you can read the file here and check your stuff
        File.Delete(path);
    }

注意 'using' 关键字。它可以让你的代码更加简洁,因为它会自动处理 Dispose 调用。


根据评论,那样做会允许不同的应用程序访问该文件吗? - mafu
1
@mafutrct - FileShare.Delete 的意思是“允许随后删除文件”。不同的应用程序可以删除它,但不能写入或从中读取。 - Simon Mourier
虽然在我的情况下,删除文件不是问题。相反,写入文件才是问题 - 我会为此提出另一个问题。 - mafu
这完全有道理,但是(正如在 OP 评论中所述),我担心它可能不会那样工作(至少在 Windows 上)。 - mafu
@mafutrct - 它确实是这样工作的。例如,您可以使用记事本轻松测试它。1)使用记事本创建一个文件,添加一些文本,保存并保持打开状态。2)运行读取此文件内容并等待键盘按键(作为您检查的占位符)的程序。3)当程序正在等待时,请尝试在记事本中再次保存,您会发现它无法工作,因为该文件被您的程序锁定。4)按下键后,您将看到文件已成功删除。这就是它的意思。正如所说的唯一缺点是另一个程序可以删除,但您说这不是问题。 - Simon Mourier
显示剩余2条评论

1
首先,你正在模仿“using”语句,但你的方法是错误的。你应该在finally子句中只Dispose文件流一次,而不是在try和catch中Dispose两次。但更好的方法是使用using语句。
using (FileStream s = new FileStream())
{
}

第二,在编程方面最好的选择是事务性NTFS(其中一个包装器可以在Nabu库中找到:https://dev.triflesoft.org/mercurial/nabu/),但是事务性NTFS仅限于NTFS和Windows Vista+,因此如果您需要FAT16/FAT32或Windows XP,则不适用。

您也可以尝试将已打开的文件移动/重命名,以防止其他进程访问,但据我所知,这仅适用于NTFS。


如果您不需要立即删除文件,则可以使用Setup API的SetupQueueDelete函数。


在 try 块内使用 dispose 是必需的,因为我无法删除一个打开的文件。 - mafu
@mafutrct 使用 close 而不是 dispose。 - Scott Chamberlain
@ScottChamberlain:我不明白那有什么帮助。Don't Close和Dispose对于FileStream来说不是同样的代码吗? - mafu
我删除了之前的评论,因为在反编译后发现它是错误的。所以我将其删除并重新表述了一下。 - Scott Chamberlain
@BryanCrosby:是的,这就是我一开始这样做的原因。不过在这里使用+Close似乎更加清晰易读易懂。(但Dispose两次在技术上并不是一个错误) - mafu
显示剩余3条评论

0

你无法完全防止竞态条件的可能性。考虑到如果在检查和删除文件之间文件被修改时,你的程序就会出现问题,我至少可以看到两个解决方法:

  • 获取临时文件名,将文件重命名为临时文件,在需要时进行检查并重新命名回来(这可能会根据你的业务逻辑导致新的问题)
  • 在检查文件之前,您可以设置文件的只读属性。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接