【问题标题】:Creating temporary folders创建临时文件夹
【发布时间】:2008-08-19 18:14:14
【问题描述】:

我正在开发一个需要为应用程序创建多个临时文件夹的程序。这些将不会被用户看到。该应用程序是用 VB.net 编写的。我可以想到一些方法来做到这一点,例如增量文件夹名称或随机编号的文件夹名称,但我想知道,其他人如何解决这个问题?

【问题讨论】:

    标签: .net io


    【解决方案1】:

    更新:为每条评论添加了 File.Exists 检查(2012 年 6 月 19 日)

    这是我在 VB.NET 中使用的。基本上与介绍的相同,除了我通常不想立即创建文件夹。

    使用GetRandomFilename 的优点是它不会创建文件,因此如果您将名称用于文件以外的其他内容,则无需进行清理。就像使用它作为文件夹名称一样。

    Private Function GetTempFolder() As String
        Dim folder As String = Path.Combine(Path.GetTempPath, Path.GetRandomFileName)
        Do While Directory.Exists(folder) or File.Exists(folder)
            folder = Path.Combine(Path.GetTempPath, Path.GetRandomFileName)
        Loop
    
        Return folder
    End Function
    

    随机文件名示例:

    C:\Documents and Settings\用户名\Local Settings\Temp\u3z5e0co.tvq


    这是一个使用 Guid 获取临时文件夹名称的变体。

    Private Function GetTempFolderGuid() As String
        Dim folder As String = Path.Combine(Path.GetTempPath, Guid.NewGuid.ToString)
        Do While Directory.Exists(folder) or File.Exists(folder)
            folder = Path.Combine(Path.GetTempPath, Guid.NewGuid.ToString)
        Loop
    
        Return folder
    End Function
    

    guid 示例:

    C:\Documents and Settings\用户名\Local Settings\Temp\2dbc6db7-2d45-4b75-b27f-0bd492c60496

    【讨论】:

    • 除了Directory.Exists之外,您的循环条件是否应该包括File.ExistsPath.GetTempPath 中可能存在一个现有的临时 文件,这将阻止创建目录。
    • 你是对的。我将更新答案以包含 File.Exists() 检查
    • 您应该在函数中创建文件夹——与 GetTempFileName 创建文件的原因相同:确保其他人这样做的可能性为零。
    • 记得做Imports System.IO
    【解决方案2】:

    你必须使用System.IO.Path.GetTempFileName()

    在磁盘上创建一个唯一命名的零字节临时文件并返回该文件的完整路径。

    您可以使用System.IO.Path.GetDirectoryName(System.IO.Path.GetTempFileName()) 仅获取临时文件夹信息,并在其中创建您的文件夹

    它们是在 windows 临时文件夹中创建的,这被认为是最佳实践

    【讨论】:

    • 不要不要这样做。在System.IO.Path.GetTempFileName 上调用System.IO.Path.GetDirectoryNameSystem.IO.Path.GetTempPath 最糟糕的替代品,因为它会创建一个被遗忘的文件。
    【解决方案3】:

    澄清一下:

    System.IO.Path.GetTempPath()
    

    只返回临时文件夹的文件夹路径。

    System.IO.Path.GetTempFileName()
    

    返回完全限定的文件名(包括路径),因此:

    System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetTempFileName())
    

    是多余的。

    【讨论】:

    • GetTempPath 和 GetTempFileName 是两种完全不同的方法。 GetTempPath 只返回系统临时文件夹的位置,而 GetTempFileName 创建一个临时的零长度文件并返回该文件的完整路径。
    【解决方案4】:

    在以下情况下可能存在竞争条件:

    • 使用GetTempFileName() 创建一个临时文件,将其删除,然后创建一个同名文件夹,或者
    • 使用GetRandomFileName()Guid.NewGuid.ToString 命名文件夹并稍后创建文件夹

    删除发生后使用GetTempFileName(),另一个应用程序可以成功创建同名的临时文件。然后CreateDirectory() 会失败。

    同样,在调用GetRandomFileName() 和创建目录之间,另一个进程可能会创建同名的文件或目录,再次导致CreateDirectory() 失败。

    对于大多数应用程序,临时目录由于竞争条件而失败是可以的。毕竟是极其罕见的。对他们来说,这些种族往往可以忽略不计。

    在 Unix shell 脚本世界中,以安全、无竞争的方式创建临时文件和目录是一件大事。许多机器有多个(恶意)用户——想想​​共享的网络主机——许多脚本和应用程序需要在共享的 /tmp 目录中安全地创建临时文件和目录。有关如何从 shell 脚本安全地创建临时目录的讨论,请参阅 Safely Creating Temporary Files in Shell Scripts

    【讨论】:

      【解决方案5】:

      作为@JonathanWright pointed out,解决方案存在竞争条件:

      • GetTempFileName()创建一个临时文件,删除它,再创建一个同名文件夹
      • 使用GetRandomFileName()Guid.NewGuid.ToString随机创建文件夹名,检查是否存在,不存在则创建。

      但是,可以通过使用 Transactional NTFS (TxF) API 以原子方式创建唯一的临时目录。

      TxF 有一个CreateDirectoryTransacted() 函数,可以通过 Platform Invoke 调用。为此,我将Mohammad Elsheimy's code 改编为调用CreateFileTransacted()

      // using System.ComponentModel;
      // using System.Runtime.InteropServices;
      // using System.Transactions;
      
      [ComImport]
      [Guid("79427a2b-f895-40e0-be79-b57dc82ed231")]
      [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
      public interface IKernelTransaction
      {
          void GetHandle(out IntPtr pHandle);
      }
      
      // 2.2 Win32 Error Codes <http://msdn.microsoft.com/en-us/library/cc231199.aspx>
      public const int ERROR_PATH_NOT_FOUND = 0x3;
      public const int ERROR_ALREADY_EXISTS = 0xb7;
      public const int ERROR_EFS_NOT_ALLOWED_IN_TRANSACTION = 0x1aaf;
      
      [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
      public static extern bool CreateDirectoryTransacted(string lpTemplateDirectory, string lpNewDirectory, IntPtr lpSecurityAttributes, IntPtr hTransaction);
      
      /// <summary>
      /// Creates a uniquely-named directory in the directory named by <paramref name="tempPath"/> and returns the path to it.
      /// </summary>
      /// <param name="tempPath">Path of a directory in which the temporary directory will be created.</param>
      /// <returns>The path of the newly-created temporary directory within <paramref name="tempPath"/>.</returns>
      public static string GetTempDirectoryName(string tempPath)
      {
          string retPath;
      
          using (TransactionScope transactionScope = new TransactionScope())
          {
              IKernelTransaction kernelTransaction = (IKernelTransaction)TransactionInterop.GetDtcTransaction(Transaction.Current);
              IntPtr hTransaction;
              kernelTransaction.GetHandle(out hTransaction);
      
              while (!CreateDirectoryTransacted(null, retPath = Path.Combine(tempPath, Path.GetRandomFileName()), IntPtr.Zero, hTransaction))
              {
                  int lastWin32Error = Marshal.GetLastWin32Error();
                  switch (lastWin32Error)
                  {
                      case ERROR_ALREADY_EXISTS:
                          break;
                      default:
                          throw new Win32Exception(lastWin32Error);
                  }
              }
      
              transactionScope.Complete();
          }
          return retPath;
      }
      
      /// <summary>
      /// Equivalent to <c>GetTempDirectoryName(Path.GetTempPath())</c>.
      /// </summary>
      /// <seealso cref="GetTempDirectoryName(string)"/>
      public static string GetTempDirectoryName()
      {
          return GetTempDirectoryName(Path.GetTempPath());
      }
      

      【讨论】:

        【解决方案6】:

        类似...

        using System.IO;
        
        string path = Path.GetTempPath() + Path.GetRandomFileName();
        while (Directory.Exists(path))
         path = Path.GetTempPath() + Path.GetRandomFileName();
        
        Directory.CreateDirectory(path);
        

        【讨论】:

        • 如果创建目录失败,可能会使用 try-catch 重新进入循环 ;-)
        【解决方案7】:

        您可以为您的临时文件夹名称生成一个 GUID。

        【讨论】:

          【解决方案8】:

          只要文件夹的名称不需要有意义,为它们使用 GUID 怎么样?

          【讨论】:

            【解决方案9】:

            您可以使用GetTempFileName 创建一个临时文件,然后删除该文件并将其重新创建为目录。

            注意:链接失效,复制/粘贴自:http://msdn.microsoft.com/en-us/library/aa364991(VS.85).aspx

            【讨论】:

              【解决方案10】:

              @adam-wright 和 pix0r 的综合答案将是最好的恕我直言:

              
              using System.IO;
              
              string path = Path.GetTempPath() + Path.GetRandomFileName();
              
              while (Directory.Exists(path)) 
                path = Path.GetTempPath() + Path.GetRandomFileName();
              
              File.Delete(path);
              Directory.CreateDirectory(path);
              

              【讨论】:

                【解决方案11】:

                使用 System.IO.Path.GetTempFileName 的优点是它将是用户本地(即非漫游)路径中的文件。出于权限和安全原因,这正是您想要的。

                【讨论】:

                  【解决方案12】:
                  Dim NewFolder = System.IO.Directory.CreateDirectory(IO.Path.Combine(IO.Path.GetTempPath, Guid.NewGuid.ToString))
                  

                  【讨论】:

                    【解决方案13】:

                    @JonathanWright 建议 CreateDirectory 在已有文件夹时会失败。如果我阅读 Directory.CreateDirectory 它会说“无论指定路径上的目录是否已经存在,都会返回此对象。”这意味着您没有检测到在检查存在和实际创建之间创建的文件夹。

                    我喜欢@DanielTrebbien 建议的 CreateDirectoryTransacted(),但该功能已被弃用。

                    我看到的唯一解决方案是使用 c api 并在那里调用“CreateDirectory”,因为如果您确实需要确保覆盖整个竞争条件,如果文件夹存在,它会出错。 这将导致这样的事情:

                    Private Function GetTempFolder() As String
                        Dim folder As String
                        Dim succes as Boolean = false
                        Do While not succes
                            folder = Path.Combine(Path.GetTempPath, Path.GetRandomFileName)
                            success = c_api_create_directory(folder)
                        Loop
                        Return folder
                    End Function
                    

                    【讨论】:

                    • 检查无限循环是明智的。
                    猜你喜欢
                    • 1970-01-01
                    • 2016-06-30
                    • 1970-01-01
                    • 2011-04-24
                    • 1970-01-01
                    • 2018-08-17
                    • 2018-01-18
                    • 2018-02-09
                    • 1970-01-01
                    相关资源
                    最近更新 更多