Создание временных папок
Я работаю над программой, которая должна создать несколько временных папок для приложения. Они не будут видны пользователю. Приложение написано на VB.net. Я могу придумать несколько способов сделать это, например, добавочное имя папки или случайное нумерованное имя папки, но мне было интересно, как другие люди решают эту проблему?
13 ответов
Обновление: добавлена проверка 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 \ имя пользователя \ Локальные настройки \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 \ имя пользователя \ Локальные настройки \Temp\2dbc6db7-2d45-4b75-b27f-0bd492c60496
Вы должны использовать System.IO.Path.GetTempFileName()
Создает на диске временный файл с нулевым байтом с уникальным именем и возвращает полный путь к этому файлу.
Ты можешь использовать System.IO.Path.GetDirectoryName(System.IO.Path.GetTempFileName())
чтобы получить только информацию о временных папках и создать там свои папки
Они создаются во временной папке Windows, и это считается наилучшей практикой
Возможны условия гонки, когда:
- создание временного файла с
GetTempFileName()
, удалив его и создав папку с тем же именем, или - с помощью
GetRandomFileName()
или жеGuid.NewGuid.ToString
назвать папку и создать папку позже
С GetTempFileName()
после удаления другое приложение может успешно создать временный файл с тем же именем. CreateDirectory()
тогда потерпит неудачу.
Точно так же между звонками GetRandomFileName()
и создание каталога, другой процесс может создать файл или каталог с тем же именем, что снова приводит к CreateDirectory()
терпит неудачу.
Для большинства приложений это нормально для временного каталога, чтобы потерпеть неудачу из-за условия гонки. Это крайне редко в конце концов. Для них эти гонки часто можно игнорировать.
В мире сценариев оболочки Unix создание временных файлов и каталогов безопасным способом без гонок является большой проблемой. У многих машин есть несколько (враждебных) пользователей - например, общий веб-хостинг - и многие сценарии и приложения должны безопасно создавать временные файлы и каталоги в каталоге shared / tmp. См. Безопасное создание временных файлов в сценариях оболочки для обсуждения того, как безопасно создавать временные каталоги из сценариев оболочки.
Просто для ясности:
System.IO.Path.GetTempPath()
возвращает только путь к папке temp.
System.IO.Path.GetTempFileName()
возвращает полное имя файла (включая путь), так что это:
System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetTempFileName())
избыточно
Как отметил @JonathanWright, для решений существуют условия гонки:
- Создать временный файл с
GetTempFileName()
, удалите его и создайте папку с тем же именем - использование
GetRandomFileName()
или жеGuid.NewGuid.ToString
чтобы создать случайное имя папки, проверьте, существует ли оно, и создайте его, если нет.
Однако можно создать уникальный временный каталог атомарно, используя Transactional NTFS (TxF) API.
TxF имеет CreateDirectoryTransacted()
функция, которая может быть вызвана через Платформу 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);
Вы можете использовать GetTempFileName для создания временного файла, а затем удалить и заново создать этот файл как каталог.
Примечание: ссылка не работает, скопируйте / вставьте с: 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);
Пока имя папки не должно быть значимым, как насчет использования GUID для них?
@JonathanWright предполагает, что CreateDirectory потерпит неудачу, когда папка уже есть. Если я читаю Directory.CreateDirectory, он говорит: "Этот объект возвращается независимо от того, существует ли каталог по указанному пути". Это означает, что вы не обнаруживаете папку, созданную между проверкой, существует и фактически создает.
Мне нравится CreateDirectoryTransacted(), предложенный @DanielTrebbien, но эта функция устарела.
Единственное оставшееся решение, которое я вижу, состоит в том, чтобы использовать 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
Преимущество использования System.IO.Path.GetTempFileName заключается в том, что это будет файл с локальным (то есть не роуминг) путем пользователя. Это именно то место, где вы хотели бы получить его по разрешениям и по соображениям безопасности.
Dim NewFolder = System.IO.Directory.CreateDirectory(IO.Path.Combine(IO.Path.GetTempPath, Guid.NewGuid.ToString))