正在使用的文件如何删除?

28

我写了一个简单的C#程序来删除临时文件(只是出于兴趣,不是主要项目),但是遇到了被锁定的(正在使用)文件问题。通常如何排除这些文件?参考一下我收到的错误:

该进程无法访问文件“ExchangePerflog_8484fa31c65c7a31cfcccd43.dat”,因为它正被另一个进程使用。

代码:

static void Main(string[] args)
    {
        string folderPath = string.Empty;
        folderPath = System.Environment.GetEnvironmentVariable("temp");
        deleteFilesInDirectory(folderPath);
    }

    public static void deleteFilesInDirectory(string folderPath) 
    {

        try
        {
            var dir = new DirectoryInfo(folderPath);
            dir.Attributes = dir.Attributes & ~FileAttributes.ReadOnly;
            dir.Delete(true);
            MessageBox.Show(folderPath + " has been cleaned.");
        }
        catch (System.IO.IOException ex)
        {
            MessageBox.Show(ex.Message); 
            return;

        } 
    }     

1
请澄清一下,您是想跳过正在使用的文件,还是试图强制删除它们? - Tim
为什么不直接收集未删除的文件并在执行后显示它们?或者你需要等待它们变得可访问吗? - cookieMonster
这个问题已经有答案了,请查看以下链接:https://dev59.com/ym025IYBdhLWcg3wUESP - sergiogarciadev
还有一个实用工具叫做“Unlocker” - http://www.emptyloop.com/unlocker/。它可以释放锁定(并告诉您是谁锁定了它们),从而允许您删除它们。这更适用于调查而不是一般使用。 - dash
更好的解决方案是通过由计划程序启动的批处理文件,在启动时删除所有临时文件。 - i486
显示剩余2条评论
7个回答

23

描述

无法删除当前正在被其他进程使用的文件。但你可以等待文件不再被锁定。

使用此方法在循环中检查文件是否已解锁。

protected virtual bool IsFileLocked(FileInfo file)
{
    FileStream stream = null;

    try
    {
        stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
    }
    catch (IOException)
    {
        //the file is unavailable because it is:
        //still being written to
        //or being processed by another thread
        //or does not exist (has already been processed)
        return true;
    }
    finally
    {
        if (stream != null)
            stream.Close();
    }

    //file is not locked
    return false;
}

例子

FileInfo file = new FileInfo("PathToTheFile");
while (IsFileLocked(file))
    Thread.Sleep(1000);
file.Delete();

更新

如果你想跳过被锁定的文件,可以这样做。

//
var dir = new DirectoryInfo(folderPath);
foreach(var file in dir.GetFiles()) {
    try
    {
        file.Delete();
    }
    catch (IOException)
    {
        //file is currently locked
    }
}

如果例如我在机器运行时删除所有临时文件,并且保持Outlook打开状态,那么Outlook拥有的临时文件直到关闭Outlook才会被删除。因此,使用这种方法,程序将继续尝试执行,直到关闭Outlook。我更喜欢检查它是否正在使用,并在使用时跳过它。 - Geekender
这种方法的问题在于,当IsFileLocked返回false时,意味着文件未被锁定,但它可能会被执行验证的代码再次短暂地锁定。在Windows上可以很容易地观察到这一点。 - BartoszKP

14

尝试以下代码。只需在文件删除前添加两行代码:

GC.Collect();
GC.WaitForPendingFinalizers();

1
尝试删除正在被另一个进程使用的文件时,这将没有任何影响。 - crashmstr
这与文件删除无关,只是指示 .Net 框架强制进行垃圾回收。 - Chris
3
如果你使用SQLite,那么这个评论是完全有效的。请参见https://dev59.com/LGoy5IYBdhLWcg3wfeMR。 - Moeri
2
哇,这个问题被投了这么多反对票。这确实可能是你无法删除文件的原因。在我的情况下,我有很多线程正在使用一个文件(内存映射)。在处理完所有的内存映射线程并清理它们之后,我试图删除映射文件。但是它不允许我这样做,因为垃圾回收器没有时间在删除发生之前进行清理。这解决了我的问题,+1! - Arvo Bowen
我也修复了它,尽管这不是实际的问题,在我看来,许多人在使用另一个进程后仍然会遇到文件被阻止的问题。 - c_ph_r

10

我遇到了类似的问题。当你在删除文件后不久尝试删除目录时,你需要强制垃圾回收器释放当前线程的文件句柄。

public void DisposeAfterTest(string filePath)
    {

        if (File.Exists(filePath))
        {
            File.Delete(filePath);
        }

        GC.Collect();
        GC.WaitForPendingFinalizers();

        if (Directory.Exists(this.TempTestFolderPath))
        {
            Directory.Delete(this.TempTestFolderPath, true);
        }

    }

2
这是没有将所有文件访问类包装在“using”中的症状。正确处理文件访问句柄的释放,您就不会遇到这个问题了。 - Liam

3
使用dknaack的代码。在我的情况下,这个方法有效。
FileInfo file = new FileInfo("xyz.txt");
try
{
    for (int tries = 0; IsFileLocked(file) && tries < 5; tries++)
        Thread.Sleep(1000);
    file.Delete();
}
catch (IOException exception)
{
    Console.WriteLine(string.Format("File locked: {0}", exception);
}

1

我不相信你能提前知道文件是否正在使用。你可以尝试对文件进行独占锁定,但这只会将一个异常换成另一个异常。

如果这些是你正在打开的文件,请尽量做好关闭它们的工作。如果情况比较复杂,你可以维护一个“待删除列表”,并继续重试删除操作,直到成功为止(也许可以在另一个线程中使用并发集合)。

我也不相信有任何强制删除正在使用文件的方法。


有没有类似于catch(System.IO.FileInUse)这样的东西? - Geekender

0
下面的代码将从目录及其所有子目录中删除文件,但排除被锁定的文件,并列出未被删除的文件列表。如果只考虑当前目录,请将SearchOption更改为TopDirectoryOnly。
string []files = Directory.GetFiles(dirPath,"*.*", SearchOption.AllDirectories); //this gets the files in all subdirectories as well
List<string> lockedFiles = new List<string>();
foreach(string file in files) 
{    
    try    
    {        
        file.Delete();    
    }    
    catch (IOException)    
    {        
        lockedFiles.Add(file);    
    }
}

1
@NineTails,他不是IEnumerable<T>,而是一个数组。 - Shimmy Weitzhandler

0
只需要调用一个方法,即WipeFile,代码如下所示。所以,你真正需要做的就是调用WipeFile并提供要删除的文件的完整路径,以及您要覆盖它的次数。
public void WipeFile(string filename, int timesToWrite)
{
    try
    {
        if (File.Exists(filename))
        {
            // Set the files attributes to normal in case it's read-only.

            File.SetAttributes(filename, FileAttributes.Normal);

            // Calculate the total number of sectors in the file.
            double sectors = Math.Ceiling(new FileInfo(filename).Length/512.0);

            // Create a dummy-buffer the size of a sector.

            byte[] dummyBuffer = new byte[512];

            // Create a cryptographic Random Number Generator.
            // This is what I use to create the garbage data.

            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();

            // Open a FileStream to the file.
            FileStream inputStream = new FileStream(filename, FileMode.Open);
            for (int currentPass = 0; currentPass < timesToWrite; currentPass++)
            {
                UpdatePassInfo(currentPass + 1, timesToWrite);

                // Go to the beginning of the stream

                inputStream.Position = 0;

                // Loop all sectors
                for (int sectorsWritten = 0; sectorsWritten < sectors; sectorsWritten++)
                {
                    UpdateSectorInfo(sectorsWritten + 1, (int) sectors);

                    // Fill the dummy-buffer with random data

                    rng.GetBytes(dummyBuffer);

                    // Write it to the stream
                    inputStream.Write(dummyBuffer, 0, dummyBuffer.Length);
                }
            }

            // Truncate the file to 0 bytes.
            // This will hide the original file-length if you try to recover the file.

            inputStream.SetLength(0);

            // Close the stream.
            inputStream.Close();

            // As an extra precaution I change the dates of the file so the
            // original dates are hidden if you try to recover the file.

            DateTime dt = new DateTime(2037, 1, 1, 0, 0, 0);
            File.SetCreationTime(filename, dt);
            File.SetLastAccessTime(filename, dt);
            File.SetLastWriteTime(filename, dt);

            // Finally, delete the file

            File.Delete(filename);

            WipeDone();
        }
    }
    catch(Exception e)
    {
        WipeError(e);
    }
}

我添加了一些事件,以便能够跟踪过程中发生的情况。

  • PassInfoEvent - 返回正在运行的传递和要运行的总传递数。
  • SectorInfoEvent - 返回正在写入的扇区和要写入的总扇区数。
  • WipeDoneEvent - 表示擦除过程已完成。
  • WipeErrorEvent - 如果出现任何问题,则返回异常。

使用.NET安全地删除文件


1
如果文件被锁定(如OP中所述),您无法向其写入或删除它 - 无论是尝试使用OP代码还是此代码,您都会收到System.IO.IOException:'The process cannot access the file...'。这是针对“如何安全地删除未锁定的文件”的答案,而不是所问的问题。 - Jinlye

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