Який найкращий спосіб отримати ім'я тимчасового каталогу в 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;
}
Якщо вам справді потрібне криптографічно захищене випадкове ім’я, можливо, ви захочете адаптувати відповідь Скотта до певного часу або робити цикл, щоб намагатися створити шлях на диску.
Мені подобається використовувати GetTempPath (), функцію створення GUID на зразок CoCreateGuid () та CreateDirectory ().
GUID розроблений так, щоб мати високу ймовірність унікальності, а також дуже малоймовірно, щоб хтось вручну створив каталог з тією ж формою, що і GUID (а якщо це буде, то CreateDirectory () не зможе вказати на його існування.)
@Chris. Я теж був одержимий віддаленим ризиком того, що тимчасовий каталог вже може існувати. Дискусії про випадкові та криптографічно сильні теж мене повністю не задовольняють.
Мій підхід базується на фундаментальному факті, що O / S не повинен дозволяти 2 дзвінки створити файл, щоб обоє були успішними. Мало дивно, що дизайнери .NET вирішили приховати функціональність API Win32 для каталогів, що робить це набагато простіше, оскільки він повертає помилку при спробі створення каталогу вдруге. Ось що я використовую:
[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.
Якщо ви плануєте використовувати каталог temp як засіб збереження даних у програмі, вам, мабуть, слід поглянути на використання IsolatedStorage як сховища для конфігурації / стану / тощо ...