Как лучше всего получить имя временного каталога в Windows? Я вижу, что могу использовать GetTempPath
и GetTempFileName
для создания временного файла, но есть ли какой-либо эквивалент функции Linux / BSD mkdtemp
для создания временного каталога?
Как лучше всего получить имя временного каталога в Windows? Я вижу, что могу использовать GetTempPath
и GetTempFileName
для создания временного файла, но есть ли какой-либо эквивалент функции Linux / BSD mkdtemp
для создания временного каталога?
Ответы:
Нет, нет эквивалента mkdtemp. Наилучший вариант - использовать комбинацию GetTempPath и GetRandomFileName .
Вам понадобится код, подобный этому:
public string GetTemporaryDirectory()
{
string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory(tempDirectory);
return tempDirectory;
}
Я взламываю, Path.GetTempFileName()
чтобы указать действительный псевдослучайный путь к файлу на диске, затем удаляю файл и создаю каталог с тем же путем.
Это позволяет избежать необходимости проверять, доступен ли путь к файлу в течение некоторого времени или цикла, согласно комментарию Криса к ответу Скотта Дормана.
public string GetTemporaryDirectory()
{
string tempFolder = Path.GetTempFileName();
File.Delete(tempFolder);
Directory.CreateDirectory(tempFolder);
return tempFolder;
}
Если вам действительно нужно криптографически безопасное случайное имя, вы можете адаптировать ответ Скотта, чтобы использовать цикл while или do, чтобы продолжать попытки создать путь на диске.
Мне нравится использовать GetTempPath (), функцию создания GUID, такую как CoCreateGuid () и CreateDirectory ().
GUID разработан с высокой вероятностью уникальности, и также крайне маловероятно, чтобы кто-то вручную создал каталог с той же формой, что и GUID (и если они это сделают, CreateDirectory () не сможет указать его существование.)
@Крис. Я тоже был одержим удаленным риском того, что временный каталог может уже существовать. Разговоры о случайном и криптографически стойком тоже меня не полностью удовлетворяют.
Мой подход основан на фундаментальном факте, что операционная система не должна позволять двум вызовам создавать файл, чтобы оба были успешными. Немного удивительно, что разработчики .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 (). Будьте осторожны при получении родительского элемента каталога, поскольку в корне он равен нулю.
Обычно я использую это:
/// <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.
GetTempPath - правильный способ сделать это; Я не уверен, что вас беспокоит по поводу этого метода. Затем вы можете использовать CreateDirectory, чтобы сделать это.
Вот несколько более грубый подход к решению проблемы коллизии для временных имен каталогов. Это не безошибочный подход, но он значительно снижает вероятность столкновения пути к папке.
Потенциально можно добавить к имени каталога другую информацию, относящуюся к процессу или сборке, чтобы сделать конфликт еще менее вероятным, хотя отображение такой информации во временном имени каталога может быть нежелательным. Можно также изменить порядок комбинирования полей, связанных со временем, чтобы имена папок выглядели более случайными. Я лично предпочитаю оставить это так просто потому, что мне легче найти их все во время отладки.
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);
Как упоминалось выше, Path.GetTempPath () - один из способов сделать это. Вы также можете вызвать Environment.GetEnvironmentVariable ("TEMP"), если у пользователя настроена переменная среды TEMP.
Если вы планируете использовать временный каталог в качестве средства сохранения данных в приложении, вам, вероятно, следует рассмотреть возможность использования IsolatedStorage в качестве репозитория для конфигурации / состояния / и т. Д.