Windows临时文件行为 – 他们是否被系统删除?

使用.net框架,您可以select创build临时文件

Path.GetTempFileName(); 

MSDN不会告诉我们临时文件会发生什么情况。 我记得当它重新启动的时候,在某个地方被操作系统删除了。 这是真的?

如果文件没有被操作系统删除,为什么他们被称为临时? 它们是正常目录中的普通文件。

简短的回答:他们不会被删除。

长答案:pipe理Path.GetTempFileName()方法调用本机Win32API GetTempFileName()方法,如下所示:

 //actual .NET 2.0 decompiled code // .NET Reflector rocks for looking at plumbing public static string GetTempFileName() { string tempPath = GetTempPath(); new FileIOPermission(FileIOPermissionAccess.Write, tempPath).Demand(); StringBuilder tmpFileName = new StringBuilder(260); if (Win32Native.GetTempFileName(tempPath, "tmp", 0, tmpFileName) == 0) { __Error.WinIOError(); } return tmpFileName.ToString(); } 

本地方法的文档指出:

由该function创build的临时文件不会自动删除。 要删除这些文件,请调用DeleteFile。

我发现了一篇名为“那些讨厌的临时文件” (Archived Oct. 2007)的文章,它从基础开始,触及一些不太明显的处理临时文件的问题,例如:

  • 如何确保文件被删除(即使应用程序崩溃!提示: FileOption.DeleteOnClose并让内核处理它)
  • 如何获得正确的文件caching策略,以提高性能(提示: FileAttributes.Temporary
  • 如何确保文件的内容保持安全,因为:
    • 使用托pipe方法的文件名比使用非托pipe方法更具可预测性
    • 临时文件被创build,然后closures ,然后你得到它的path(只能打开它),从而留下一个恶意代码/用户劫持文件的机会的小窗口。

来自文章的C#代码:

 using System; using System.IO; using System.Security.Permissions; using System.Security.Principal; using System.Security.AccessControl; public static class PathUtility { private const int defaultBufferSize = 0x1000; // 4KB #region GetSecureDeleteOnCloseTempFileStream /// <summary> /// Creates a unique, randomly named, secure, zero-byte temporary file on disk, which is automatically deleted when it is no longer in use. Returns the opened file stream. /// </summary> /// <remarks> /// <para>The generated file name is a cryptographically strong, random string. The file name is guaranteed to be unique to the system's temporary folder.</para> /// <para>The <see cref="GetSecureDeleteOnCloseTempFileStream"/> method will raise an <see cref="IOException"/> if no unique temporary file name is available. Although this is possible, it is highly improbable. To resolve this error, delete all uneeded temporary files.</para> /// <para>The file is created as a zero-byte file in the system's temporary folder.</para> /// <para>The file owner is set to the current user. The file security permissions grant full control to the current user only.</para> /// <para>The file sharing is set to none.</para> /// <para>The file is marked as a temporary file. File systems avoid writing data back to mass storage if sufficient cache memory is available, because an application deletes a temporary file after a handle is closed. In that case, the system can entirely avoid writing the data. Otherwise, the data is written after the handle is closed.</para> /// <para>The system deletes the file immediately after it is closed or the <see cref="FileStream"/> is finalized.</para> /// </remarks> /// <returns>The opened <see cref="FileStream"/> object.</returns> public static FileStream GetSecureDeleteOnCloseTempFileStream() { return GetSecureDeleteOnCloseTempFileStream(defaultBufferSize, FileOptions.DeleteOnClose); } /// <summary> /// Creates a unique, randomly named, secure, zero-byte temporary file on disk, which is automatically deleted when it is no longer in use. Returns the opened file stream with the specified buffer size. /// </summary> /// <remarks> /// <para>The generated file name is a cryptographically strong, random string. The file name is guaranteed to be unique to the system's temporary folder.</para> /// <para>The <see cref="GetSecureDeleteOnCloseTempFileStream"/> method will raise an <see cref="IOException"/> if no unique temporary file name is available. Although this is possible, it is highly improbable. To resolve this error, delete all uneeded temporary files.</para> /// <para>The file is created as a zero-byte file in the system's temporary folder.</para> /// <para>The file owner is set to the current user. The file security permissions grant full control to the current user only.</para> /// <para>The file sharing is set to none.</para> /// <para>The file is marked as a temporary file. File systems avoid writing data back to mass storage if sufficient cache memory is available, because an application deletes a temporary file after a handle is closed. In that case, the system can entirely avoid writing the data. Otherwise, the data is written after the handle is closed.</para> /// <para>The system deletes the file immediately after it is closed or the <see cref="FileStream"/> is finalized.</para> /// </remarks> /// <param name="bufferSize">A positive <see cref="Int32"/> value greater than 0 indicating the buffer size.</param> /// <returns>The opened <see cref="FileStream"/> object.</returns> public static FileStream GetSecureDeleteOnCloseTempFileStream(int bufferSize) { return GetSecureDeleteOnCloseTempFileStream(bufferSize, FileOptions.DeleteOnClose); } /// <summary> /// Creates a unique, randomly named, secure, zero-byte temporary file on disk, which is automatically deleted when it is no longer in use. Returns the opened file stream with the specified buffer size and file options. /// </summary> /// <remarks> /// <para>The generated file name is a cryptographically strong, random string. The file name is guaranteed to be unique to the system's temporary folder.</para> /// <para>The <see cref="GetSecureDeleteOnCloseTempFileStream"/> method will raise an <see cref="IOException"/> if no unique temporary file name is available. Although this is possible, it is highly improbable. To resolve this error, delete all uneeded temporary files.</para> /// <para>The file is created as a zero-byte file in the system's temporary folder.</para> /// <para>The file owner is set to the current user. The file security permissions grant full control to the current user only.</para> /// <para>The file sharing is set to none.</para> /// <para>The file is marked as a temporary file. File systems avoid writing data back to mass storage if sufficient cache memory is available, because an application deletes a temporary file after a handle is closed. In that case, the system can entirely avoid writing the data. Otherwise, the data is written after the handle is closed.</para> /// <para>The system deletes the file immediately after it is closed or the <see cref="FileStream"/> is finalized.</para> /// <para>Use the <paramref name="options"/> parameter to specify additional file options. You can specify <see cref="FileOptions.Encrypted"/> to encrypt the file contents using the current user account. Specify <see cref="FileOptions.Asynchronous"/> to enable overlapped I/O when using asynchronous reads and writes.</para> /// </remarks> /// <param name="bufferSize">A positive <see cref="Int32"/> value greater than 0 indicating the buffer size.</param> /// <param name="options">A <see cref="FileOptions"/> value that specifies additional file options.</param> /// <returns>The opened <see cref="FileStream"/> object.</returns> public static FileStream GetSecureDeleteOnCloseTempFileStream(int bufferSize, FileOptions options) { FileStream fs = GetSecureFileStream(Path.GetTempPath(), bufferSize, options | FileOptions.DeleteOnClose); File.SetAttributes(fs.Name, File.GetAttributes(fs.Name) | FileAttributes.Temporary); return fs; } #endregion #region GetSecureTempFileStream public static FileStream GetSecureTempFileStream() { return GetSecureTempFileStream(defaultBufferSize, FileOptions.None); } public static FileStream GetSecureTempFileStream(int bufferSize) { return GetSecureTempFileStream(bufferSize, FileOptions.None); } public static FileStream GetSecureTempFileStream(int bufferSize, FileOptions options) { FileStream fs = GetSecureFileStream(Path.GetTempPath(), bufferSize, options); File.SetAttributes(fs.Name, File.GetAttributes(fs.Name) | FileAttributes.NotContentIndexed | FileAttributes.Temporary); return fs; } #endregion #region GetSecureTempFileName public static string GetSecureTempFileName() { return GetSecureTempFileName(false); } public static string GetSecureTempFileName(bool encrypted) { using (FileStream fs = GetSecureFileStream(Path.GetTempPath(), defaultBufferSize, encrypted ? FileOptions.Encrypted : FileOptions.None)) { File.SetAttributes(fs.Name, File.GetAttributes(fs.Name) | FileAttributes.NotContentIndexed | FileAttributes.Temporary); return fs.Name; } } #endregion #region GetSecureFileName public static string GetSecureFileName(string path) { return GetSecureFileName(path, false); } public static string GetSecureFileName(string path, bool encrypted) { using (FileStream fs = GetSecureFileStream(path, defaultBufferSize, encrypted ? FileOptions.Encrypted : FileOptions.None)) { return fs.Name; } } #endregion #region GetSecureFileStream public static FileStream GetSecureFileStream(string path) { return GetSecureFileStream(path, defaultBufferSize, FileOptions.None); } public static FileStream GetSecureFileStream(string path, int bufferSize) { return GetSecureFileStream(path, bufferSize, FileOptions.None); } public static FileStream GetSecureFileStream(string path, int bufferSize, FileOptions options) { if (path == null) throw new ArgumentNullException("path"); if (bufferSize <= 0) throw new ArgumentOutOfRangeException("bufferSize"); if ((options & ~(FileOptions.Asynchronous | FileOptions.DeleteOnClose | FileOptions.Encrypted | FileOptions.RandomAccess | FileOptions.SequentialScan | FileOptions.WriteThrough)) != FileOptions.None) throw new ArgumentOutOfRangeException("options"); new FileIOPermission(FileIOPermissionAccess.Write, path).Demand(); SecurityIdentifier user = WindowsIdentity.GetCurrent().User; FileSecurity fileSecurity = new FileSecurity(); fileSecurity.AddAccessRule(new FileSystemAccessRule(user, FileSystemRights.FullControl, AccessControlType.Allow)); fileSecurity.SetAccessRuleProtection(true, false); fileSecurity.SetOwner(user); // Attempt to create a unique file three times before giving up. // It is highly improbable that there will ever be a name clash, // therefore we do not check to see if the file first exists. for (int attempt = 0; attempt < 3; attempt++) { try { return new FileStream(Path.Combine(path, Path.GetRandomFileName()), FileMode.CreateNew, FileSystemRights.FullControl, FileShare.None, bufferSize, options, fileSecurity); } catch (IOException) { if (attempt == 2) throw; } } // This code can never be reached. // The compiler thinks otherwise. throw new IOException(); } #endregion } 

根据我的%tmp%中的March文件,我不会说。

为什么他们被称为临时 – 因为这是他们的预期用法。 他们不是系统文件; 它们不是应用程序文件,它们不是用户文档…它们只允许应用程序进行临时处理(可能在大量数据上),或者经常通过IPC将数据传递到另一个进程。 因此,他们确实是暂时的。

你应该旨在删除你创build的任何临时文件,致命的“杀”等等不能抵挡。 我经常使用“使用” – 我创build一个包装类 – 即

 sealed class TempFile : IDisposable { // formatted for space string path; public string Path { get { if(path==null) throw new ObjectDisposedException(GetType().Name); return path; } } public TempFile() : this(System.IO.Path.GetTempFileName()) { } public TempFile(string path) { if (string.IsNullOrEmpty(path)) throw new ArgumentNullException("path"); this.path = path; } private void Dispose(bool disposing) { if (path != null) { try { File.Delete(path); } catch { } // best endeavours... path = null; } } public void Dispose() { GC.SuppressFinalize(this); Dispose(true); } ~TempFile() { Dispose(false); } } 

有一个FileOptions.DeleteOnClose选项,可以做你想做的。

这里是MSDN页面的链接。

不,这是不正确的。 基本上,你的应用程序负责清理自己的烂摊子。 如果你不这样做,临时文件将随着时间的推移而累积。

不,它在软件的责任(阅读:开发者),创build一个临时文件来处理它。

看看你自己的临时文件夹,看看它是如何工作;-)

它们被称为临时的,因为在大多数情况下,用户可以假定她可以安全地清理临时目录中的垃圾。如果不是,通常这些文件总是被locking。

一般来说,这些文件应该是短暂的:创build它们,将它们用于任何需要的位置,然后当场删除它们。 更糟糕的是,在退出应用程序时将其删除。

有时候,你不能,例如。 一个档案pipe理器或VCS允许用编辑器(或差异查看器等)查看文件,但在编辑器之前closures(或不能监视产生的过程…)。

我已经在互联网上读了很多次,人们不想使用Path.GetTempFileName因为他们说它可以返回一个已经存在的文件,来解决这个问题,你可以根据一个GUID做一个文件名。

这个函数解决了这个问题:迭代直到find一个不存在的具有特定扩展名的文件名。

VB.net

 Public Shared Function GetTempFileName(ByVal extensionWithDot As String) As String Dim tempFileName As String Do tempFileName = System.IO.Path.GetTempFileName If extensionWithDot IsNot Nothing Then tempFileName = tempFileName.Replace(System.IO.Path.GetExtension(tempFileName), extensionWithDot) End If Loop While System.IO.File.Exists(tempFileName) Return tempFileName End Function 

C#:

 public static string GetTempFileName(string extensionWithDot) { string tempFileName = null; do { tempFileName = System.IO.Path.GetTempFileName; if (extensionWithDot != null) { tempFileName = tempFileName.Replace(System.IO.Path.GetExtension(tempFileName), extensionWithDot); } } while (System.IO.File.Exists(tempFileName)); return tempFileName; } 

注意:我使用argumentWithDot参数,因为System.IO.Path.GetExtension返回点。