web-dev-qa-db-fra.com

Créer un répertoire temporaire sous Windows?

Quel est le meilleur moyen d'obtenir un nom de répertoire temporaire sous Windows? Je vois que je peux utiliser GetTempPath et GetTempFileName pour créer un fichier temporaire, mais existe-t-il un équivalent de la fonction Linux/BSD mkdtemp pour créer un répertoire temporaire?

115
Josh Kelley

Non, il n'y a pas d'équivalent à mkdtemp. La meilleure option consiste à utiliser une combinaison de GetTempPath et GetRandomFileName .

Vous auriez besoin d'un code similaire à ceci:

public string GetTemporaryDirectory()
{
   string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
   Directory.CreateDirectory(tempDirectory);
   return tempDirectory;
}
214
Scott Dorman

Je pirate Path.GetTempFileName() pour me donner un chemin de fichier valide, pseudo-aléatoire sur le disque, puis supprime le fichier et crée un répertoire avec le même chemin.

Cela évite de vérifier si le chemin du fichier est disponible dans une boucle while ou while, comme le dit Chris à propos de la réponse de Scott Dorman.

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

  return tempFolder;
}

Si vous avez vraiment besoin d'un nom aléatoire cryptographiquement sécurisé, vous voudrez peut-être adapter la réponse de Scott pour qu'elle utilise une boucle while ou do pour continuer à essayer de créer un chemin sur le disque.

20
Steve Jansen

J'aime utiliser GetTempPath (), une fonction de création de GUID telle que CoCreateGuid () et CreateDirectory ().

Un GUID est conçu pour avoir une forte probabilité d'unicité et il est également très improbable que quelqu'un crée manuellement un répertoire avec le même formulaire qu'un GUID ( et si c'est le cas, CreateDirectory () échouera en indiquant son existence.)

7
Matthew

@Chris. Moi aussi, j'étais obsédé par le risque à distance qu'un répertoire temporaire puisse déjà exister. Les discussions sur le hasard et la force cryptographique ne me satisfont pas complètement non plus.

Mon approche repose sur le fait fondamental que le système d’exploitation ne doit pas autoriser 2 appels à créer un fichier pour réussir. Il est un peu surprenant que les concepteurs .NET aient choisi de masquer la fonctionnalité de l'API Win32 pour les répertoires, ce qui facilite grandement la tâche, car elle renvoie une erreur lorsque vous essayez de créer un répertoire pour la deuxième fois. Voici ce que j'utilise:

    [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;
    }

Vous décidez si le "coût/risque" d'un code p/invoke non géré en vaut la peine. La plupart diraient que non, mais au moins, vous avez maintenant le choix.

CreateParentFolder () est laissé comme exercice à l'étudiant. J'utilise Directory.CreateDirectory (). Soyez prudent en obtenant le parent d'un répertoire, car il est nul lorsqu'il est à la racine.

5
Andrew Dennison

J'utilise habituellement ceci:

    /// <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;
    }

Si vous voulez être absolument sûr que ce nom de répertoire n'existera pas dans le chemin temporaire, vous devez vérifier si ce nom de répertoire unique existe et essayer d'en créer un autre s'il existe réellement.

Mais cette implémentation basée sur un GUID est suffisante. Je n'ai aucune expérience avec aucun problème dans ce cas. Certaines applications MS utilisent également les répertoires temporaires basés sur GUID).

2
Jan Hlavsa

GetTempPath est la bonne façon de le faire. Je ne suis pas sûr de votre préoccupation à propos de cette méthode. Vous pouvez ensuite utiliser CreateDirectory pour le créer.

2
Will

Voici une approche un peu plus brutale pour résoudre le problème de collision pour les noms de répertoire temporaires. Ce n'est pas une approche infaillible, mais cela réduit considérablement les risques de collision entre les chemins des dossiers.

Il est possible que d'autres noms liés au processus ou à l'assembly soient éventuellement ajoutés au nom du répertoire afin de rendre la collision encore moins probable, bien que rendre une telle information visible sur le nom du répertoire temporaire puisse ne pas être souhaitable. Vous pouvez également mélanger l'ordre dans lequel les champs temporels sont combinés pour que les noms de dossiers aient l'air plus aléatoires. Personnellement, je préfère le laisser simplement parce qu'il m'est plus facile de tous les trouver lors du débogage.

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);
1
Paulo de Barros

Comme mentionné ci-dessus, Path.GetTempPath () est un moyen de le faire. Vous pouvez également appeler Environment.GetEnvironmentVariable ("TEMP") si l'utilisateur a défini une variable d'environnement TEMP.

Si vous envisagez d'utiliser le répertoire temporaire comme moyen de conserver des données dans l'application, vous devriez probablement envisager d'utiliser IsolatedStorage comme référentiel pour la configuration/l'état/etc ...

0
Chris Rauber