Создание временного каталога в Windows? - PullRequest
112 голосов
/ 10 ноября 2008

Какой лучший способ получить имя временного каталога в Windows? Я вижу, что могу использовать GetTempPath и GetTempFileName для создания временного файла, но есть ли какой-нибудь эквивалент функции Linux / BSD mkdtemp для создания временного каталога?

Ответы [ 8 ]

207 голосов
/ 10 ноября 2008

Нет, нет эквивалента mkdtemp. Лучше всего использовать комбинацию GetTempPath и GetRandomFileName .

Вам понадобится код, подобный следующему:

public string GetTemporaryDirectory()
{
   string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
   Directory.CreateDirectory(tempDirectory);
   return tempDirectory;
}
20 голосов
/ 08 декабря 2013

Я взломал Path.GetTempFileName(), чтобы дать мне правильный псевдослучайный путь к файлу на диске, затем удалил файл и создал каталог с тем же путем к файлу.

Это избавляет от необходимости проверять, доступен ли путь к файлу через некоторое время или в цикле, согласно комментарию Криса к ответу Скотта Дормана.

public string GetTemporaryDirectory()
{
  string tempFolder = Path.GetTempFileName();
  File.Delete(tempFolder);
  Directory.CreateDirectory(tempFolder);

  return tempFolder;
}

Если вам действительно нужно криптографически безопасное случайное имя, вы можете адаптировать ответ Скотта, чтобы использовать цикл while или do, чтобы продолжать пытаться создать путь на диске.

7 голосов
/ 06 августа 2010

Мне нравится использовать GetTempPath (), функцию создания GUID, такую ​​как CoCreateGuid () и CreateDirectory ().

GUID предназначен для обеспечения высокой вероятности уникальности, и очень маловероятно, что кто-то вручную создаст каталог с такой же формой, как GUID (и если это произойдет, CreateDirectory () не сможет указать его существование)

5 голосов
/ 11 января 2016

@ Крис. Я тоже был одержим удаленным риском, что временный каталог может уже существовать. Дискуссии о случайных и криптографически сильных мне тоже не совсем устраивают.

Мой подход основывается на фундаментальном факте, что O / S не должен позволять 2 вызовам создать файл, чтобы оба были успешными. Немного удивительно, что разработчики .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 (). Будьте осторожны при получении родительского элемента каталога, так как в корне он равен нулю.

2 голосов
/ 10 ноября 2008

GetTempPath - правильный способ сделать это; Я не уверен, что вы беспокоитесь об этом методе. Затем вы можете использовать CreateDirectory , чтобы сделать это.

1 голос
/ 02 сентября 2016

Я обычно использую это:

    /// <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.

1 голос
/ 10 декабря 2013

Вот несколько более грубый подход к решению проблемы коллизий для временных имен каталогов. Это не безошибочный подход, но он значительно снижает вероятность столкновения пути к папке.

Можно потенциально добавить другую информацию, относящуюся к процессу или сборке, к имени каталога, чтобы сделать коллизию еще менее вероятной, хотя отображение такой информации в имени временного каталога может быть нежелательным. Можно также смешать порядок, в котором поля, связанные со временем, объединяются, чтобы имена папок выглядели более случайными. Я лично предпочитаю оставить это так просто, потому что мне легче найти их все во время отладки.

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);
0 голосов
/ 10 ноября 2008

Как упоминалось выше, Path.GetTempPath () является одним из способов сделать это. Вы также можете вызвать Environment.GetEnvironmentVariable ("TEMP") , если для пользователя установлена ​​переменная среды TEMP.

Если вы планируете использовать временный каталог в качестве средства сохранения данных в приложении, вам, вероятно, следует рассмотреть возможность использования IsolatedStorage в качестве хранилища для конфигурации / состояния / и т.д ...

...