在Windows中创建一个临时目录?


126

在Windows中获取临时目录名称的最佳方法是什么?我看到可以使用GetTempPathGetTempFileName创建一个临时文件,但是是否有等效于Linux / BSD的mkdtemp功能来创建临时目录?


这个问题似乎很难找到。特别是,如果在“堆栈溢出”搜索框中键入“ temp directory .net”之类的内容,则不会出现。这似乎是不幸的,因为到目前为止所有答案都是.NET答案。您认为您可以添加“ .net”标签吗?(也许是“目录”或“临时目录”标记?)还是在标题中添加单词“ .NET”?也许在问题正文中也用“ temporary”(临时)与“ temp”(临时)交替显示-因此,如果您搜索较短的格式,仍将获得良好的文本搜索匹配。我似乎没有足够的代表本人来做这些事情。谢谢。
克里斯,2009年

好吧,我一直在寻找非.NET的答案,所以我宁愿忽略掉它,但是我进行了您建议的其他编辑。谢谢。
乔什·凯利

@Josh Kelley:我只是仔细检查了Win32 API,唯一的选择就是遵循类似的方法来获取临时路径,生成随机文件名然后创建目录。
Scott Dorman

Answers:


230

不,没有等效于mkdtemp。最好的选择是结合使用GetTempPathGetRandomFileName

您将需要类似于以下代码:

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

3
这似乎有点危险。特别是,Path.Combine(Path.GetTempPath(),Path.GetRandomFileName())可能会返回已经存在的目录的名称(较小但非零,对吗?)。由于如果tempDirectory已经存在,Directory.CreateDirectory(tempDirectory)不会引发异常,因此您的应用程序不会检测到这种情况。然后,您可能有两个应用程序彼此协作。.NET中还有其他更安全的选择吗?
克里斯,2009年

22
@Chris:GetRandomFileName方法返回一个加密强度高的随机字符串,可用作字符串名或文件名。我想从理论上说,最终的路径可能已经存在,但是没有其他方法可以做到这一点。您可以检查路径是否存在,是否确实再次调用Path.GetRandomFileName(),然后重复。
Scott Dorman

5
@Chris:是的,如果您担心这种程度的安全性,则另一个进程很有可能在Path.Combine和Directory.CreateDirectory调用之间创建目录。
Scott Dorman

8
GetRandomFileName生成11个随机的小写字母和数字,这意味着域大小为(26 + 10)^ 11 =〜57位。您可以随时拨打两次电话
马蒂·尼尔

27
在您检查目录不存在之后,上帝可能会暂停宇宙,潜入并使用您将要编写的所有相同文件创建相同目录,从而导致您的应用崩溃,以至于破坏了您的一天。
Triynko

25

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

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

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

  return tempFolder;
}

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


7

我喜欢使用GetTempPath(),GUID创建功能(如CoCreateGuid()和CreateDirectory())。

GUID被设计为具有很高的唯一性可能性,而且极不可能有人用与GUID相同的形式手动创建目录(如果这样做,则CreateDirectory()将失败以指示其存在。)


5

@克里斯。我也很迷恋可能已经存在一个临时目录的远程风险。关于随机性和加密性强的讨论也没有完全令我满意。

我的方法基于以下基本事实:操作系统不允许两个调用创建一个文件才能成功。.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 /调用代码的“成本/风险”是否值得。大多数人会说不是,但是至少您现在可以选择。

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


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的临时目录。


1

GetTempPath是正确的方法。我不确定您对此方法有何顾虑。然后,您可以使用CreateDirectory进行创建。


一个问题是GetTempFileName将创建一个零字节的文件。您需要改用GetTempPath,GetRandomFileName和CreateDirectory。
Scott Dorman

好的,可能的和可行的。我打算提供代码,但Dorman在我之前得到了它,并且可以正常工作。

1

这是解决临时目录名称冲突问题的一种更蛮力的方法。这不是一个可靠的方法,但是它可以大大减少文件夹路径冲突的机会。

一个人可能会在目录名中添加其他与进程或程序集相关的信息,以使冲突的可能性更小,尽管使这种信息在临时目录名上可见是不希望的。也可以混合使用与时间相关的字段的顺序,以使文件夹名称看起来更加随机。我个人更喜欢以这种方式保留它,因为在调试过程中我更容易找到它们。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.