【问题标题】:Creating a temporary directory in Windows?在 Windows 中创建临时目录?
【发布时间】:2010-09-21 15:54:07
【问题描述】:

在 Windows 中获取临时目录名称的最佳方法是什么?我看到我可以使用GetTempPathGetTempFileName来创建临时文件,但是有没有相当于Linux/BSD的mkdtemp函数来创建临时目录?

【问题讨论】:

  • 这个问题似乎有点难找。特别是,如果您在 Stack Overflow 搜索框中键入“temp directory .net”之类的内容,它不会显示。这似乎很不幸,因为到目前为止答案都是 .NET 的答案。你认为你可以添加“.net”标签吗? (也许是“目录”或“临时目录”标签?)或者可能在标题中添加“.NET”这个词?也可能在问题正文中交替使用“temp”和“temporary”——因此,如果您搜索较短的形式,您仍然会得到很好的文本搜索匹配。我似乎没有足够的代表自己做这些事情。谢谢。
  • 好吧,我正在寻找一个非 .NET 的答案,所以我宁愿把它排除在外,但我做了你建议的其他编辑。谢谢。
  • @Josh Kelley:我刚刚仔细检查了 Win32 API,唯一的选择是采用类似的方法来获取临时路径,生成随机文件名,然后创建目录。

标签: c# .net windows temporary-directory


【解决方案1】:

不,没有与 mkdtemp 等效的东西。最好的选择是使用GetTempPathGetRandomFileName 的组合。

你需要类似这样的代码:

public string GetTemporaryDirectory()
{
   string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
   Directory.CreateDirectory(tempDirectory);
   return tempDirectory;
}

【讨论】:

  • 这似乎有点危险。特别是,Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()) 有可能(很小,但非零,对吗?)将返回已经存在的目录的名称。由于如果 tempDirectory 已经存在,Directory.CreateDirectory(tempDirectory) 不会引发异常,因此您的应用程序不会检测到这种情况。然后你可能有两个应用程序互相踩踏对方的工作。 .NET 中是否有更安全的替代方案?
  • @Chris:GetRandomFileName 方法返回一个加密强度高的随机字符串,可用作文件夹名称或文件名称。我想理论上可能生成的路径可能已经存在,但没有其他方法可以做到这一点。您可以检查路径是否存在,如果它确实再次调用 Path.GetRandomFileName(),然后重复。
  • @Chris:是的,如果您担心安全性到那种程度,那么另一个进程可能在 Path.Combine 和 Directory 之间创建目录的可能性非常 .CreateDirectory 调用。
  • GetRandomFileName 生成 11 个随机小写字母和数字,表示域大小为 (26+10)^11 = ~57 位。你总是可以打两次电话来平方它
  • 在你检查目录不存在之后,上帝可能会暂停宇宙,潜入并用你要写的所有相同文件创建相同的目录,导致你的应用程序崩溃,只是为了毁了你的一天。
【解决方案2】:

我破解Path.GetTempFileName() 给我一个有效的、伪随机磁盘上的文件路径,然后删除该文件,并创建一个具有相同文件路径的目录。

根据 Chris 对 Scott Dorman 的回答的评论,这避免了检查文件路径是否在一段时间或循环中可用的需要。

public string GetTemporaryDirectory()
{
  string tempFolder = Path.GetTempFileName();
  File.Delete(tempFolder);
  Directory.CreateDirectory(tempFolder);

  return tempFolder;
}

如果您确实需要一个加密安全的随机名称,您可能需要调整 Scott 的答案以使用 while 或 do loop 来继续尝试在磁盘上创建路径。

【讨论】:

  • 难道还有人在DeleteCreateDirectory之间创建同一个目录吗?
【解决方案3】:

我喜欢使用 GetTempPath()、一个 GUID 创建函数,如 CoCreateGuid() 和 CreateDirectory()。

GUID 被设计为具有很高的唯一性,而且也极不可能有人手动创建与 GUID 具有相同形式的目录(如果他们这样做了,那么 CreateDirectory() 将无法表明它的存在。)

【讨论】:

    【解决方案4】:

    @克里斯。我也沉迷于临时目录可能已经存在的远程风险。关于随机和密码强度的讨论也不能完全让我满意。

    我的方法建立在这样一个基本事实之上,即操作系统不能允许 2 次调用来创建文件都成功。 .NET 设计人员选择隐藏目录的 Win32 API 功能有点令人惊讶,这使得这更容易,因为当您第二次尝试创建目录时它会返回错误。这是我使用的:

        [DllImport(@"kernel32.dll", EntryPoint = "CreateDirectory", SetLastError = true, CharSet = CharSet.Unicode)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool CreateDirectoryApi
            ([MarshalAs(UnmanagedType.LPTStr)] string lpPathName, IntPtr lpSecurityAttributes);
    
        /// <summary>
        /// Creates the directory if it does not exist.
        /// </summary>
        /// <param name="directoryPath">The directory path.</param>
        /// <returns>Returns false if directory already exists. Exceptions for any other errors</returns>
        /// <exception cref="System.ComponentModel.Win32Exception"></exception>
        internal static bool CreateDirectoryIfItDoesNotExist([NotNull] string directoryPath)
        {
            if (directoryPath == null) throw new ArgumentNullException("directoryPath");
    
            // First ensure parent exists, since the WIN Api does not
            CreateParentFolder(directoryPath);
    
            if (!CreateDirectoryApi(directoryPath, lpSecurityAttributes: IntPtr.Zero))
            {
                Win32Exception lastException = new Win32Exception();
    
                const int ERROR_ALREADY_EXISTS = 183;
                if (lastException.NativeErrorCode == ERROR_ALREADY_EXISTS) return false;
    
                throw new System.IO.IOException(
                    "An exception occurred while creating directory'" + directoryPath + "'".NewLine() + lastException);
            }
    
            return true;
        }
    

    您可以决定非托管 p/invoke 代码的“成本/风险”是否值得。大多数人会说不是,但至少你现在有一个选择。

    CreateParentFolder() 留给学生作为练习。我使用 Directory.CreateDirectory()。获取目录的父目录时要小心,因为它在根目录时为空。

    【讨论】:

      【解决方案5】:

      我通常用这个:

          /// <summary>
          /// Creates the unique temporary directory.
          /// </summary>
          /// <returns>
          /// Directory path.
          /// </returns>
          public string CreateUniqueTempDirectory()
          {
              var uniqueTempDir = Path.GetFullPath(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()));
              Directory.CreateDirectory(uniqueTempDir);
              return uniqueTempDir;
          }
      

      如果您想绝对确定临时路径中不存在此目录名,则需要检查此唯一目录名是否存在,如果确实存在,请尝试创建另一个。

      但是这种基于 GUID 的实现就足够了。在这种情况下,我没有任何问题的经验。一些 MS 应用程序也使用基于 GUID 的临时目录。

      【讨论】:

        【解决方案6】:

        我使用了一些答案,并以这种方式实现了GetTmpDirectory 方法。

        public string GetTmpDirectory()
        {
            string tmpDirectory;
        
            do
            {
                tmpDirectory = Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(Path.GetRandomFileName()));
            } while (Directory.Exists(tmpDirectory));
        
            Directory.CreateDirectory(tmpDirectory);
            return tmpDirectory;
        }
        

        【讨论】:

          【解决方案7】:

          这是解决临时目录名称冲突问题的一种更暴力的方法。这不是一种万无一失的方法,但它显着降低了文件夹路径冲突的机会。

          可以将其他进程或程序集相关信息添加到目录名称中,以降低冲突的可能性,尽管可能不希望在临时目录名称上显示此类信息。还可以混合时间相关字段的组合顺序,以使文件夹名称看起来更随机。我个人更喜欢这样保留它,因为在调试过程中我更容易找到它们。

          string randomlyGeneratedFolderNamePart = Path.GetFileNameWithoutExtension(Path.GetRandomFileName());
          
          string timeRelatedFolderNamePart = DateTime.Now.Year.ToString()
                                           + DateTime.Now.Month.ToString()
                                           + DateTime.Now.Day.ToString()
                                           + DateTime.Now.Hour.ToString()
                                           + DateTime.Now.Minute.ToString()
                                           + DateTime.Now.Second.ToString()
                                           + DateTime.Now.Millisecond.ToString();
          
          string processRelatedFolderNamePart = System.Diagnostics.Process.GetCurrentProcess().Id.ToString();
          
          string temporaryDirectoryName = Path.Combine( Path.GetTempPath()
                                                      , timeRelatedFolderNamePart 
                                                      + processRelatedFolderNamePart 
                                                      + randomlyGeneratedFolderNamePart);
          

          【讨论】:

            【解决方案8】:

            如上所述,Path.GetTempPath() 是一种方法。如果用户设置了 TEMP 环境变量,您也可以调用 Environment.GetEnvironmentVariable("TEMP")

            如果您打算使用 temp 目录作为在应用程序中保存数据的一种方式,您可能应该考虑使用 IsolatedStorage 作为配置/状态/等的存储库...

            【讨论】:

              【解决方案9】:

              GetTempPath 是正确的做法;我不确定您对这种方法的关注是什么。然后你可以使用CreateDirectory 来制作它。

              【讨论】:

              • 一个问题是 GetTempFileName 将创建一个零字节文件。您需要改用 GetTempPath、GetRandomFileName 和 CreateDirectory。
              • 这很好,可能而且可行。我打算提供代码,但 Dorman 在我之前得到了它,而且它工作正常。
              猜你喜欢
              • 2017-03-06
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-11-07
              相关资源
              最近更新 更多