如何在c#中自动删除临时文件?

Nif*_*fle 55 .net c# temporary-files

如果我的应用程序关闭或崩溃,有什么方法可以确保删除临时文件?理想情况下,我想获取一个临时文件,使用它,然后忘记它.

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

有没有更好的办法?

Mar*_*ell 76

如果过程被过早杀死,则using无法保证,但是,我使用" "来执行此操作.

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,当TempFile处理或垃圾收集时,文件将被删除(如果可能).显然,您可以根据需要使用它,或者在某个集合中使用它.

  • 您通常不会看到使用空挡块的适当位置. (8认同)
  • @KevinPanko因为我没有花时间考虑所有可能的继承方案,以确定它在子类化时是否会按预期运行.如果你想这样做,请随意, (4认同)
  • @PMBjornerud是的,在这种情况下; 这里终结器的目的与`File`对象有关.它与类的意图有关,这是(见问题)**完成后删除文件**.我们想要这样做*即使*某人没有'正确地'Dispose()`它.我们甚至没有*一个`File`实例. (2认同)

Dan*_*anW 57

考虑使用FileOptions.DeleteOnClose标志:

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
Run Code Online (Sandbox Code Playgroud)

  • 即使设置了 FileOptions.DeleteOnClose 并且不考虑 FileShare.ReadWrite,另一个进程也无法访问创建的文件。所以总的来说这段代码用作临时文件没有意义。 (3认同)

Dav*_*ant 18

你可以P/InvokeCreateFile并传递FILE_FLAG_DELETE_ON_CLOSE旗帜.这告诉Windows在关闭所有句柄后删除文件.另请参阅:Win32 CreateFile文档.

  • 如果您需要多次打开文件(例如稍后或其他过程),请小心使用此方法.例如,如果您希望打开,填充和关闭文件,它可能会立即消失,因此您无法使用刚才创建的内容.您必须将原始句柄保持打开足够长的时间才能打开第二个句柄,但如果未使用适当的标志打开,则可能导致共享冲突.在.NET 4中(可能更早),只需使用File.Create(path,0x1000,FileOptions.DeleteOnClose); 不需要P/Invoke. (8认同)
  • 我想知道是否有办法将FileOptions.DeleteOnClose与System.Diagnostics.Process.Start(myFile)结合使用.我想显示文件并立即删除. (6认同)

小智 6

我会使用 .NETTempFileCollection类,因为它是内置的,可在旧版本的 .NET 中使用,并实现IDisposable接口,因此如果与"using"关键字结合使用,则在其自身之后进行清理。

这是从嵌入资源中提取文本的示例(通过项目属性页添加 -> 资源选项卡,如下所述:How to embed a text file in a .NET assembly?,然后"EmbeddedResource"在嵌入文件的属性设置中设置为 )。

    // 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.");
    }
Run Code Online (Sandbox Code Playgroud)


Kon*_*ard 5

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

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)) { }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

每次需要临时文件时使用:

var tempFile = TemporaryFiles.UseNew();
Run Code Online (Sandbox Code Playgroud)

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

TemporaryFiles.DeleteAllPreviouslyUsed();
Run Code Online (Sandbox Code Playgroud)

在应用程序开始时。