Windows临时文件行为 - 是否被系统删除?

时间:2008-11-27 15:34:21

标签: .net windows file

使用.net框架,您可以选择使用

创建临时文件
Path.GetTempFileName(); 

MSDN没有告诉我们临时文件会发生什么。我记得在某个地方读到它们在重启时会被操作系统删除。这是真的吗?

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

7 个答案:

答案 0 :(得分:42)

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

答案很长: 托管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();
}

本机方法的文档说明:

  

不会自动删除其名称由此函数创建的临时文件。要删除这些文件,请调用DeleteFile。

我找到了一篇名为"Those pesky temp files"(2007年10月存档)的精彩文章,该文章从基础知识开始,触及处理临时文件的一些不太明显的问题,例如:

  • 如何确保文件被删除(即使应用程序崩溃!提示:FileOption.DeleteOnClose并让内核处理它)
  • 如何为文件获取正确的缓存策略,以提高性能(提示:FileAttributes.Temporary
  • 如何确保文件内容保持安全,因为:
    • 使用托管方法比使用非托管方法更可预测文件名
    • 创建临时文件,然后关闭,然后您获取它的路径(仅重新打开它),从而为恶意代码/用户劫持文件留下一个小机会窗口。

文章中的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

}

答案 1 :(得分:8)

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

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

你的目标应该是删除你创建的任何临时文件,致命的“杀死”等等。我经常使用“使用” - 我创建了一个包装类 - 即。

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);
    }
}

答案 2 :(得分:5)

有一个 FileOptions.DeleteOnClose 选项可能会执行您想要的操作。

以下是MSDN页面的链接。

答案 3 :(得分:1)

不,这不是真的。基本上,您的应用程序负责清理自己的混乱。如果不这样做,临时文件会随着时间累积。

答案 4 :(得分:0)

不,软件(读取:开发人员)负责创建一个临时文件来处理它。

查看您自己的临时文件夹,看看它的效果如何; - )

答案 5 :(得分:0)

它们被称为临时的,因为在大多数情况下,用户可以假设她可以安全地清理临时目录中的混乱......如果不是,通常情况下,这些文件都会被锁定。

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

有时,你不能,例如。存档管理器或VCS允许使用编辑器(或差异查看器等)查看文件,但在编辑器之前关闭(或者无法监视生成的进程...)。

答案 6 :(得分:-2)

我很多时候在互联网上读过人们不想使用Path.GetTempFileName,因为他们说它可能会返回一个已经存在的文件,以解决你可以根据一个文件建立文件名的问题GUID。

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

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;
}

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