创建临时文件夹

Creating temporary folders

提问人:wusher 提问时间:8/20/2008 最后编辑:Dan Halbertwusher 更新时间:9/13/2016 访问量:30412

问:

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

.net io

评论


答:

1赞 jwalkerjr 8/20/2008 #1

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

2赞 Andrew Rimmer 8/20/2008 #2

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

1赞 pix0r 8/20/2008 #3

可以使用 GetTempFileName 创建临时文件,然后删除并重新创建此文件作为目录。

注意:链接不起作用,请从以下位置复制/粘贴:http://msdn.microsoft.com/en-us/library/aa364991(VS.85).aspx

19赞 juan 8/20/2008 #4

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

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

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

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

评论

2赞 Jaykul 3/30/2017
不要这样做。调用是最糟糕的替代品,因为它会创建一个被遗忘的文件。System.IO.Path.GetDirectoryNameSystem.IO.Path.GetTempFileNameSystem.IO.Path.GetTempPath
3赞 Adam Wright 8/20/2008 #5

比如...

using System.IO;

string path = Path.GetTempPath() + Path.GetRandomFileName();
while (Directory.Exists(path))
 path = Path.GetTempPath() + Path.GetRandomFileName();

Directory.CreateDirectory(path);

评论

0赞 Jaykul 3/30/2017
如果您无法创建目录,也许可以使用 try-catch 重新进入循环 ;-)
1赞 Brian G Swanson 8/20/2008 #6

恕我直言,@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);
0赞 denis phillips 8/20/2008 #7

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

28赞 Rick 8/20/2008 #8

更新:添加了每条评论的 File.Exists 检查 (2012-Jun-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

评论

1赞 William Gross 6/18/2012
你的循环条件不应该包括除了?中可能存在一个现有的临时文件,这将阻止创建目录。File.ExistsDirectory.ExistsPath.GetTempPath
0赞 Rick 6/20/2012
你是对的。我将更新答案以包含 File.Exists() 检查
0赞 Jaykul 3/30/2017
您应该在函数中创建文件夹 -- 原因与 GetTempFileName 创建文件的原因相同:以确保其他人这样做的可能性为零。
0赞 Richie Bendall 6/22/2018
记得做Imports System.IO
8赞 urini 8/22/2008 #9

澄清一下:

System.IO.Path.GetTempPath()

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

System.IO.Path.GetTempFileName()

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

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

是多余的。

评论

7赞 Dries Van Hansewijck 12/14/2010
GetTempPath 和 GetTempFileName 是 2 个完全不同的方法。GetTempPath 仅返回系统临时文件夹的位置,而 GetTempFileName 创建一个临时的零长度文件并返回该文件的完整路径。
8赞 Jonathan Wright 10/20/2008 #10

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

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

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

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

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

在 Unix shell 脚本世界中,以安全、无竞争的方式创建临时文件和目录是一件大事。许多计算机有多个(敌对)用户(例如共享 Web 主机),并且许多脚本和应用程序需要在共享的 /tmp 目录中安全地创建临时文件和目录。有关如何从 shell 脚本安全地创建临时目录的讨论,请参阅在 shell 脚本中安全地创建临时文件

6赞 Daniel Trebbien 1/6/2012 #11

正如@JonathanWright所指出的,解决方案存在竞争条件:

  • 使用 创建一个临时文件,将其删除,然后创建一个同名的文件夹GetTempFileName()
  • 使用 或 创建随机文件夹名称,检查它是否存在,如果不存在,则创建它。GetRandomFileName()Guid.NewGuid.ToString

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

TxF 有一个 CreateDirectoryTransacted() 函数,可以通过平台调用调用。为此,我改编了 Mohammad Elsheimy 的调用代码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());
}
0赞 jri 9/3/2013 #12
Dim NewFolder = System.IO.Directory.CreateDirectory(IO.Path.Combine(IO.Path.GetTempPath, Guid.NewGuid.ToString))
0赞 Wilco BT 9/13/2016 #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

评论

0赞 Matt 11/9/2017
对无限循环的检查是明智的。