我使用。net 3.5,试图递归删除目录使用:

Directory.Delete(myPath, true);

我的理解是,如果文件正在使用或存在权限问题,这应该抛出,但否则它应该删除目录及其所有内容。

然而,我偶尔会遇到这样的情况:

System.IO.IOException: The directory is not empty.
    at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
    at System.IO.Directory.DeleteHelper(String fullPath, String userPath, Boolean recursive)
    at System.IO.Directory.Delete(String fullPath, String userPath, Boolean recursive)
    ...

我并不惊讶于这个方法有时会抛出错误,但我惊讶于当递归为真时得到这个特定的消息。(我知道目录不是空的。)

是否有一个原因,我将看到这个而不是AccessViolationException?


当前回答

你不必创建一个额外的递归方法或删除额外文件夹内的文件。这些都是通过调用自动完成的

DirectoryInfo.Delete ();

详情在这里。

像这样的东西效果很好:

  var directoryInfo = new DirectoryInfo("My directory path");
    // Delete all files from app data directory.

    foreach (var subDirectory in directoryInfo.GetDirectories())
    {
          subDirectory.Delete(true);// true set recursive paramter, when it is true delete sub file and sub folder with files too
    }

将true作为变量传递给delete方法,将删除包含文件的子文件和子文件夹。

其他回答

我在使用TFS2012的构建服务器上使用Windows Workflow Foundation时遇到过同样的问题。在内部,工作流调用Directory.Delete()并将递归标志设置为true。在我们的案例中,这似乎与网络有关。

在用最新的二进制文件重新创建和重新填充网络共享上的二进制文件之前,我们正在删除它。其他的构建都会失败。在构建失败后打开删除文件夹时,文件夹为空,这表明directory. delete()调用的每个方面都是成功的,除了删除实际的目录。

这个问题似乎是由网络文件通信的异步特性引起的。构建服务器告诉文件服务器删除所有文件,文件服务器报告它已经删除了,即使它还没有完全完成。然后构建服务器请求删除目录,而文件服务器拒绝了该请求,因为它还没有完全完成文件的删除。

在我们的案例中有两个可能的解决方案:

在我们自己的代码中建立递归删除,每个步骤之间都有延迟和验证 在IOException发生后重试最多X次,在再次尝试之前给予延迟

后一种方法又快又脏,但似乎很管用。

如果您的应用程序(或任何其他应用程序)的当前目录是您试图删除的目录,这将不是访问冲突错误,而是目录不是空的。 通过更改当前目录,确保它不是您自己的应用程序;同时,确保该目录没有在其他程序中打开(例如Word, excel, Total Commander等)。大多数程序将cd到最后打开的文件的目录,这将导致这种情况。

现代异步回答

公认的答案是完全错误的,它可能适用于某些人,因为从磁盘获取文件所花费的时间释放了锁定文件的任何东西。事实上,这是因为文件被其他进程/流/操作锁定了。其他答案使用Thread。Sleep (Yuck)在一段时间后重试删除目录。这个问题需要一个更现代的答案来重新审视。

public static async Task<bool> TryDeleteDirectory(
   string directoryPath,
   int maxRetries = 10,
   int millisecondsDelay = 30)
{
    if (directoryPath == null)
        throw new ArgumentNullException(directoryPath);
    if (maxRetries < 1)
        throw new ArgumentOutOfRangeException(nameof(maxRetries));
    if (millisecondsDelay < 1)
        throw new ArgumentOutOfRangeException(nameof(millisecondsDelay));

    for (int i = 0; i < maxRetries; ++i)
    {
        try
        {
            if (Directory.Exists(directoryPath))
            {
                Directory.Delete(directoryPath, true);
            }

            return true;
        }
        catch (IOException)
        {
            await Task.Delay(millisecondsDelay);
        }
        catch (UnauthorizedAccessException)
        {
            await Task.Delay(millisecondsDelay);
        }
    }

    return false;
}

单元测试

这些测试展示了一个被锁定的文件如何导致目录的示例。删除失败以及上面的TryDeleteDirectory方法如何修复这个问题。

[Fact]
public async Task TryDeleteDirectory_FileLocked_DirectoryNotDeletedReturnsFalse()
{
    var directoryPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
    var subDirectoryPath = Path.Combine(Path.GetTempPath(), "SubDirectory");
    var filePath = Path.Combine(directoryPath, "File.txt");

    try
    {
        Directory.CreateDirectory(directoryPath);
        Directory.CreateDirectory(subDirectoryPath);

        using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Write))
        {
            var result = await TryDeleteDirectory(directoryPath, 3, 30);
            Assert.False(result);
            Assert.True(Directory.Exists(directoryPath));
        }
    }
    finally
    {
        if (Directory.Exists(directoryPath))
        {
            Directory.Delete(directoryPath, true);
        }
    }
}

[Fact]
public async Task TryDeleteDirectory_FileLockedThenReleased_DirectoryDeletedReturnsTrue()
{
    var directoryPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
    var subDirectoryPath = Path.Combine(Path.GetTempPath(), "SubDirectory");
    var filePath = Path.Combine(directoryPath, "File.txt");

    try
    {
        Directory.CreateDirectory(directoryPath);
        Directory.CreateDirectory(subDirectoryPath);

        Task<bool> task;
        using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Write))
        {
            task = TryDeleteDirectory(directoryPath, 3, 30);
            await Task.Delay(30);
            Assert.True(Directory.Exists(directoryPath));
        }

        var result = await task;
        Assert.True(result);
        Assert.False(Directory.Exists(directoryPath));
    }
    finally
    {
        if (Directory.Exists(directoryPath))
        {
            Directory.Delete(directoryPath, true);
        }
    }
}

我在特尔斐也遇到过同样的问题。最终的结果是我自己的应用程序锁定了我想要删除的目录。不知何故,当我写入目录(一些临时文件)时,目录被锁定了。

问题在于,在删除它之前,我对它的父目录做了一个简单的更改。

我花了几个小时来解决这个问题和删除目录的其他异常。 这就是我的解

 public static void DeleteDirectory(string target_dir)
    {
        DeleteDirectoryFiles(target_dir);
        while (Directory.Exists(target_dir))
        {
            lock (_lock)
            {
                DeleteDirectoryDirs(target_dir);
            }
        }
    }

    private static void DeleteDirectoryDirs(string target_dir)
    {
        System.Threading.Thread.Sleep(100);

        if (Directory.Exists(target_dir))
        {

            string[] dirs = Directory.GetDirectories(target_dir);

            if (dirs.Length == 0)
                Directory.Delete(target_dir, false);
            else
                foreach (string dir in dirs)
                    DeleteDirectoryDirs(dir);
        }
    }

    private static void DeleteDirectoryFiles(string target_dir)
    {
        string[] files = Directory.GetFiles(target_dir);
        string[] dirs = Directory.GetDirectories(target_dir);

        foreach (string file in files)
        {
            File.SetAttributes(file, FileAttributes.Normal);
            File.Delete(file);
        }

        foreach (string dir in dirs)
        {
            DeleteDirectoryFiles(dir);
        }
    }

这段代码有很小的延迟,这对我的应用程序并不重要。但是要注意,如果要删除的目录中有很多子目录,那么延迟可能会成为一个问题。