关于.net:如何在C#中自动删除临时文件?

How do I automatically delete tempfiles in c#?

有什么好的方法可以确保在我的应用程序关闭或崩溃时删除临时文件? 理想情况下,我想获取一个临时文件,使用它,然后再忽略它。

现在,我保留了一个临时文件列表,并使用在Application.ApplicationExit上触发的事件处理程序将其删除。

有没有更好的办法?


如果过早地终止了进程,则无法保证,但是,我使用" using"来执行此操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
using System;
using System.IO;
sealed class TempFile : IDisposable
{
    string path;
    public TempFile() : this(System.IO.Path.GetTempFileName()) { }

    public TempFile(string path)
    {
        if (string.IsNullOrEmpty(path)) throw new ArgumentNullException("path");
        this.path = path;
    }
    public string Path
    {
        get
        {
            if (path == null) throw new ObjectDisposedException(GetType().Name);
            return path;
        }
    }
    ~TempFile() { Dispose(false); }
    public void Dispose() { Dispose(true); }
    private void Dispose(bool disposing)
    {
        if (disposing)
        {
            GC.SuppressFinalize(this);                
        }
        if (path != null)
        {
            try { File.Delete(path); }
            catch { } // best effort
            path = null;
        }
    }
}
static class Program
{
    static void Main()
    {
        string path;
        using (var tmp = new TempFile())
        {
            path = tmp.Path;
            Console.WriteLine(File.Exists(path));
        }
        Console.WriteLine(File.Exists(path));
    }
}

现在,当TempFile被处置或被垃圾回收时,文件将被删除(如果可能)。您显然可以随意使用它,也可以在某个地方的集合中使用它。


考虑使用FileOptions.DeleteOnClose标志:

1
2
3
4
5
6
7
8
using (FileStream fs = new FileStream(Path.GetTempFileName(),
       FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None,
       4096, FileOptions.RandomAccess | FileOptions.DeleteOnClose))
{
    // temp file exists
}

// temp file is gone


您可以P /调用CreateFile并传递FILE_FLAG_DELETE_ON_CLOSE标志。这告诉Windows在关闭所有句柄后删除文件。另请参阅:Win32 CreateFile文档。


我将使用.NET TempFileCollection类,因为它是内置的,可在旧版本的.NET中使用,并实现了IDisposable接口,因此如果使用了该类,则可以自行清理。与"using"关键字结合使用。

这是一个从嵌入式资源中提取文本的示例(通过项目属性页->资源选项卡添加,如下所述:如何将文本文件嵌入.NET程序集中?然后在嵌入式文件的属性设置中设置为"EmbeddedResource" )。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    // Extracts the contents of the embedded file, writes them to a temp file, executes it, and cleans up automatically on exit.
    private void ExtractAndRunMyScript()
    {
        string vbsFilePath;

        // By default, TempFileCollection cleans up after itself.
        using (var tempFiles = new System.CodeDom.Compiler.TempFileCollection())
        {
            vbsFilePath= tempFiles.AddExtension("vbs");

            // Using IntelliSense will display the name, but it's the file name
            // minus its extension.
            System.IO.File.WriteAllText(vbsFilePath, global::Instrumentation.Properties.Resources.MyEmbeddedFileNameWithoutExtension);

            RunMyScript(vbsFilePath);
        }

        System.Diagnostics.Debug.Assert(!File.Exists(vbsFilePath), @"Temp file""" + vbsFilePath+ @""" has not been deleted.");
    }

很高兴看到您要负责,但是如果文件不大(> 50MB),则将每个人(包括MS)保留在temp目录中将是符合您要求的。磁盘空间丰富。

正如csl所发布的,GetTempPath是必经之路。空间不足的用户将可以运行磁盘清理,并且您的文件(以及其他所有人的文件)也将被清理。


我主要不是C#程序员,但是在C ++中,我将为此使用RAII。有一些关于在C#在线中使用类似RAII的行为的提示,但大多数似乎都使用了不确定性的终结器。

我认为有一些Windows SDK函数可以创建临时文件,但是不知道它们是否在程序终止时自动删除。有GetTempPath函数,但是仅当您注销或重新启动IIRC时,那里的文件才被删除。

附言C#析构函数文档说您可以并且应该在那里释放资源,我觉得有些奇怪。如果是这样,您可以简单地删除析构函数中的临时文件,但是同样,这可能不是完全确定的。


我使用一个更可靠的解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
using System.IO;
using System.Reflection;

namespace Konard.Helpers
{
    public static partial class TemporaryFiles
    {
        private const string UserFilesListFilenamePrefix =".used-temporary-files.txt";
        static private readonly object UsedFilesListLock = new object();

        private static string GetUsedFilesListFilename()
        {
            return Assembly.GetEntryAssembly().Location + UserFilesListFilenamePrefix;
        }

        private static void AddToUsedFilesList(string filename)
        {
            lock (UsedFilesListLock)
            {
                using (var writer = File.AppendText(GetUsedFilesListFilename()))
                    writer.WriteLine(filename);
            }
        }

        public static string UseNew()
        {
            var filename = Path.GetTempFileName();
            AddToUsedFilesList(filename);
            return filename;
        }

        public static void DeleteAllPreviouslyUsed()
        {
            lock (UsedFilesListLock)
            {
                var usedFilesListFilename = GetUsedFilesListFilename();

                if (!File.Exists(usedFilesListFilename))
                    return;

                using (var listFile = File.Open(usedFilesListFilename, FileMode.Open))
                {
                    using (var reader = new StreamReader(listFile))
                    {
                        string tempFileToDelete;
                        while ((tempFileToDelete = reader.ReadLine()) != null)
                        {
                            if (File.Exists(tempFileToDelete))
                                File.Delete(tempFileToDelete);
                        }
                    }
                }

                // Clean up
                using (File.Open(usedFilesListFilename, FileMode.Truncate)) { }
            }
        }
    }
}

每次需要临时文件时:

1
var tempFile = TemporaryFiles.UseNew();

确保在应用程序关闭或崩溃后删除所有临时文件

1
TemporaryFiles.DeleteAllPreviouslyUsed();

在应用程序启动时。


您可以在启动时启动一个线程,该线程将删除"不应该"从崩溃中恢复时存在的文件。


如果要构建Windows窗体应用程序,则可以使用以下代码:

1
2
3
4
    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        File.Delete("temp.data");
    }