题
我正在开发一个需要为应用程序创建多个临时文件夹的程序。用户不会看到这些。该应用程序是用 VB.net 编写的。我可以想到几种方法来做到这一点,例如增量文件夹名称或随机编号的文件夹名称,但我想知道其他人如何解决这个问题?
解决方案
更新: 添加了对每个评论的 File.Exists 检查 (2012 年 6 月 19 日)
这是我在 VB.NET 中使用的。本质上与所呈现的相同,只是我通常不想立即创建该文件夹。
使用的优点 获取随机文件名 的一点是它不会创建文件,因此如果您将该名称用于文件以外的其他内容,则无需进行清理。就像用它作为文件夹名称一样。
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 emp\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
指导 例子:
C:\Documents and Settings\用户名\Local Settings emp\2dbc6db7-2d45-4b75-b27f-0bd492c60496
其他提示
你必须使用 System.IO.Path.GetTempFileName()
在磁盘上创建一个唯一命名的零字节临时文件并返回该文件的完整路径。
您可以使用 System.IO.Path.GetDirectoryName(System.IO.Path.GetTempFileName())
仅获取临时文件夹信息,并在其中创建文件夹
它们是在 Windows 临时文件夹中创建的,这被认为是最佳实践
只是为了澄清:
System.IO.Path.GetTempPath()
仅返回临时文件夹的文件夹路径。
System.IO.Path.GetTempFileName()
返回完全限定的文件名(包括路径),因此:
System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetTempFileName())
是多余的。
在以下情况下可能存在竞争条件:
- 创建一个临时文件
GetTempFileName()
, ,删除它,并创建一个同名的文件夹,或者 - 使用
GetRandomFileName()
或者Guid.NewGuid.ToString
命名文件夹并稍后创建该文件夹
和 GetTempFileName()
删除发生后,另一个应用程序可以成功创建同名的临时文件。这 CreateDirectory()
那么就会失败。
同样,在调用之间 GetRandomFileName()
创建目录时另一个进程可能会创建同名的文件或目录,再次导致 CreateDirectory()
失败。
对于大多数应用程序来说,临时目录由于竞争条件而失败是可以接受的。毕竟是极其罕见的。对于他们来说,这些种族往往可以被忽视。
在 Unix shell 脚本世界中,以安全、无竞争的方式创建临时文件和目录是一件大事。许多计算机有多个(敌对)用户(例如共享 Web 主机),并且许多脚本和应用程序需要在共享 /tmp 目录中安全地创建临时文件和目录。看 在 Shell 脚本中安全创建临时文件 有关如何从 shell 脚本安全创建临时目录的讨论。
正如@JonathanWright 指出, ,解决方案存在竞争条件:
- 创建一个临时文件
GetTempFileName()
, ,删除它,并创建一个同名文件夹 - 使用
GetRandomFileName()
或者Guid.NewGuid.ToString
创建随机文件夹名称,检查是否存在,不存在则创建。
然而,可以通过利用原子方式创建一个唯一的临时目录 事务性 NTFS (TXF) API。
TxF 有一个 CreateDirectoryTransacted()
可以通过 Platform Invoke 调用的函数。为此,我适应了 穆罕默德·埃尔斯海米的密码 用于呼叫 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());
}
就像是...
using System.IO;
string path = Path.GetTempPath() + Path.GetRandomFileName();
while (Directory.Exists(path))
path = Path.GetTempPath() + Path.GetRandomFileName();
Directory.CreateDirectory(path);
您可以为临时文件夹名称生成 GUID。
只要文件夹的名称不需要有意义,为它们使用 GUID 怎么样?
您可以使用 获取临时文件名 创建一个临时的 文件, ,然后删除该文件并将其重新创建为目录。
笔记:链接无效,复制/粘贴自: http://msdn.microsoft.com/en-us/library/aa364991(VS.85).aspx
@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);
使用 System.IO.Path.GetTempFileName 的优点是它将成为用户本地(即非漫游)路径中的文件。出于权限和安全原因,这正是您想要的位置。
Dim NewFolder = System.IO.Directory.CreateDirectory(IO.Path.Combine(IO.Path.GetTempPath, Guid.NewGuid.ToString))
@JonathanWright 建议当已有文件夹时 CreateDirectory 将失败。如果我读取目录。这意味着您未检测到存在和实际创建之间创建的文件夹。
我喜欢 @DanielTrebbien 建议的 CreateDirectoryTransacted() 但此函数已弃用。
我看到剩下的唯一解决方案是使用 c api 并调用 '创建目录' 那里,因为如果文件夹存在,如果您确实需要确保覆盖整个竞争条件,它会出错。这会导致这样的结果:
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