2008-08-19 14 views
28

Je travaille sur un programme qui doit créer plusieurs dossiers temporaires pour l'application. Ceux-ci ne seront pas vus par l'utilisateur. L'application est écrite sur VB.net. Je peux penser à quelques façons de le faire comme le nom de dossier incrémental ou des noms de dossiers numérotés au hasard, mais je me demandais, comment les autres personnes à résoudre ce problème?Création de dossiers temporaires

Répondre

26

Mise à jour: Ajouté File.Exists vérifier par commentaire (2012-juin-19)

Voici ce que je l'ai utilisé dans VB.NET. Essentiellement le même que présenté, sauf que je ne voulais généralement pas créer le dossier immédiatement.

L'avantage d'utiliser GetRandomFilename est qu'il ne crée pas de fichier, donc vous n'avez pas besoin de nettoyer si vous utilisez le nom pour autre chose qu'un fichier. Comme l'utiliser pour le nom du dossier.

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 

aléatoire Nom du fichier Exemple:

C: \ Documents and Settings \ nom d'utilisateur \ Local Settings \ Temp \ u3z5e0co.tvq


Voici une variante utilisant un Guid pour obtenir la nom du dossier temporaire.

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 Exemple:

C: \ Documents and Settings \ nom d'utilisateur \ Local Settings \ Temp \ 2dbc6db7-2d45-4b75-B27F-0bd492c60496

+0

Votre condition de boucle ne devrait pas inclure 'File.Exists' en plus de' Directory.Exists'? Il pourrait exister un _file_ temporaire dans Path.GetTempPath, ce qui empêcherait la création d'un répertoire. –

+0

Vous avez raison. Je vais mettre à jour la réponse pour inclure une vérification File.Exists() – Rick

+0

Vous devez ** créer ** le dossier dans la fonction - pour la même raison que GetTempFileName crée le fichier: pour s'assurer qu'il n'y a aucune chance que quelqu'un d'autre le fasse. – Jaykul

1

Tant que le nom du dossier n'a pas besoin d'être significatif, que diriez-vous d'utiliser un GUID pour eux?

2

Vous pouvez générer un GUID pour vos noms de dossiers temporaires.

20

Vous devez utiliser System.IO.Path.GetTempFileName()

crée un fichier temporaire au nom unique, zéro octet sur le disque et retourne le chemin complet de ce fichier.

Vous pouvez utiliser System.IO.Path.GetDirectoryName(System.IO.Path.GetTempFileName()) pour obtenir uniquement les informations du dossier temp, et créer vos dossiers là-bas

Ils sont créés dans le dossier Windows temp et considérons une meilleure pratique

+1

Do ** NOT ** do this. L'appel de 'System.IO.Path.GetDirectoryName' sur' System.IO.Path.GetTempFileName' est le pire remplacement de 'System.IO.Path.GetTempPath', car il crée un fichier qui reste derrière. – Jaykul

3

Quelque chose comme .. .

using System.IO; 

string path = Path.GetTempPath() + Path.GetRandomFileName(); 
while (Directory.Exists(path)) 
path = Path.GetTempPath() + Path.GetRandomFileName(); 

Directory.CreateDirectory(path); 
+0

peut-être avec un try-catch pour ré-entrer la boucle si vous ne parvenez pas à créer le répertoire ;-) – Jaykul

1

réponses combinées de @ adam-wright et pix0r fonctionnera le mieux à mon humble avis:


using System.IO; 

string path = Path.GetTempPath() + Path.GetRandomFileName(); 

while (Directory.Exists(path)) 
    path = Path.GetTempPath() + Path.GetRandomFileName(); 

File.Delete(path); 
Directory.CreateDirectory(path); 
0

L'avantage d'utiliser System.IO.Path.GetTempFileName est qu'il s'agira d'un fichier dans le chemin local de l'utilisateur (c'est-à-dire, non itinérant). C'est exactement où vous le voudriez pour des raisons de permissions et de sécurité.

8

Juste pour clarifier:

System.IO.Path.GetTempPath() 

renvoie simplement le chemin d'accès au dossier temporaire.

System.IO.Path.GetTempFileName() 

retourne le nom de fichier complet (y compris le chemin) donc ce:

System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetTempFileName()) 

est redondant.

+6

GetTempPath et GetTempFileName sont 2 méthodes complètement différentes. GetTempPath renvoie uniquement l'emplacement du dossier temporaire du système, tandis que GetTempFileName crée un fichier temporaire de longueur nulle et renvoie le chemin complet de ce fichier. –

8

Il y a une condition de course possible lorsque:

  • la création d'un fichier temporaire avec GetTempFileName(), supprimer et faire un dossier avec le même nom, ou
  • utilisant GetRandomFileName() ou Guid.NewGuid.ToString pour nommer un dossier et la création le dossier plus tard

Avec GetTempFileName() après la suppression se produit, une autre application peut créer avec succès un fichier temporaire avec le même nom. Le CreateDirectory() échouerait alors.

De même, entre l'appel GetRandomFileName() et la création du répertoire, un autre processus peut créer un fichier ou un répertoire portant le même nom, ce qui entraîne à nouveau l'échec de CreateDirectory().

Pour la plupart des applications, il est possible qu'un répertoire temporaire échoue en raison d'une condition de concurrence. C'est extrêmement rare après tout. Pour eux, ces courses peuvent souvent être ignorées.

Dans le monde des scripts shell Unix, la création de fichiers et de répertoires temporaires dans un environnement sécurisé et sécurisé est un gros problème. De nombreuses machines ont plusieurs utilisateurs (hostiles) - pensez à un hébergeur partagé - et de nombreux scripts et applications doivent créer en toute sécurité des fichiers temporaires et des répertoires dans le répertoire partagé/tmp. Voir Safely Creating Temporary Files in Shell Scripts pour une discussion sur la façon de créer en toute sécurité des répertoires temporaires à partir de scripts shell.

6

Comme @JonathanWright pointed out, les conditions de course existent pour les solutions:

  • Créer un fichier temporaire avec GetTempFileName(), supprimez-le et créez un dossier avec le même nom
  • Utilisez GetRandomFileName() ou Guid.NewGuid.ToString pour créer un nom de dossier aléatoire, vérifiez s'il existe et créez-le sinon.

Il est toutefois possible de créer un répertoire temporaire unique de manière atomique en utilisant l'API Transactional NTFS (TxF).

TxF dispose d'une fonction CreateDirectoryTransacted() pouvant être invoquée via l'appel de plate-forme.Pour ce faire, je me suis adapté Mohammad Elsheimy's code pour appeler 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()); 
} 
0
Dim NewFolder = System.IO.Directory.CreateDirectory(IO.Path.Combine(IO.Path.GetTempPath, Guid.NewGuid.ToString)) 
0

@JonathanWright suggère CreateDirectory échouera quand il y a déjà un dossier. Si je lis Directory.CreateDirectory, il est dit 'Cet objet est retourné, que le répertoire du chemin spécifié existe ou non.' Ce qui signifie que vous ne détectez pas un dossier créé entre la vérification existe et la création en cours.

J'aime le CreateDirectoryTransacted() suggéré par @DanielTrebbien mais cette fonction est obsolète.

La seule solution qui reste à faire est d'utiliser le cappi et d'y appeler le 'CreateDirectory' car il y a une erreur si le dossier existe si vous devez vraiment être sûr de couvrir toute la condition de la course. Il en résulterait quelque chose comme ceci:

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 
+0

Une vérification de la boucle à l'infini serait judicieuse. – Matt