Creazione di una directory temporanea in Windows?


126

Qual è il modo migliore per ottenere un nome di directory temporanea in Windows? Vedo che posso usare GetTempPathe GetTempFileNamecreare un file temporaneo, ma esiste qualche equivalente alla mkdtempfunzione Linux / BSD per la creazione di una directory temporanea?


Questa domanda sembra un po 'difficile da trovare. In particolare, non viene visualizzato se digiti cose come "temp directory .net" nella casella di ricerca Stack Overflow. Sembra sfortunato, dal momento che le risposte sono finora tutte le risposte .NET. Pensi di poter aggiungere il tag ".net"? (E forse il tag "directory" o "directory temporanea"?) O forse aggiungi la parola ".NET" al titolo? Forse anche nel corpo della domanda si alternano le parole "temp" con "temporaneo" - quindi se si cerca la forma più breve si otterrà comunque una buona corrispondenza nella ricerca del testo. Non mi sembra di avere abbastanza rappresentante per fare queste cose da solo. Grazie.
Chris,

Beh, stavo cercando una risposta non.NET, quindi preferirei lasciarla fuori, ma ho fatto le altre modifiche che mi hai suggerito. Grazie.
Josh Kelley,

@Josh Kelley: ho appena ricontrollato l'API Win32 e le uniche opzioni disponibili sono seguire un approccio simile per ottenere il percorso temporaneo, generare un nome di file ramdom e quindi creare una directory.
Scott Dorman,

Risposte:


230

No, non esiste un equivalente di mkdtemp. L'opzione migliore è utilizzare una combinazione di GetTempPath e GetRandomFileName .

Avresti bisogno di un codice simile a questo:

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

3
Questo sembra un po 'pericoloso. In particolare, c'è una possibilità (piccola, ma diversa da zero, giusto?) Che Path.Combine (Path.GetTempPath (), Path.GetRandomFileName ()) restituirà il nome di una directory già esistente. Poiché Directory.CreateDirectory (tempDirectory) non genererà un'eccezione se tempDirectory esiste già, questo caso non verrà rilevato dall'applicazione. E quindi potresti avere due applicazioni che si attivano a vicenda sul lavoro. Esiste un'alternativa più sicura in .NET?
Chris,

22
@Chris: il metodo GetRandomFileName restituisce una stringa casuale crittograficamente forte che può essere utilizzata come nome di cartella o nome di file. Suppongo sia teoricamente possibile che il percorso risultante potrebbe già esistere, ma non ci sono altri modi per farlo. È possibile verificare se il percorso esiste e se chiama nuovamente Path.GetRandomFileName () e ripetere.
Scott Dorman,

5
@Chris: Sì, se sei preoccupato per la sicurezza in quella misura, c'è una probabilità molto ridotta che un altro processo possa creare la directory tra le chiamate Path.Combine e Directory.CreateDirectory.
Scott Dorman,

8
GetRandomFileName genera 11 lettere e numeri minuscoli casuali, il che significa che la dimensione del dominio è (26 + 10) ^ 11 = ~ 57 bit. Puoi sempre effettuare due chiamate per quadrarlo
Marty Neal,

27
Subito dopo aver verificato che la directory non esiste, dio potrebbe mettere in pausa l'universo, intrufolarsi e creare la stessa directory con tutti gli stessi file che stai per scrivere, causando l'esplosione della tua app, solo per rovinare la tua giornata.
Triynko,

25

Ho hackerato Path.GetTempFileName()per darmi un percorso file pseudo-casuale valido su disco, quindi eliminare il file e creare una directory con lo stesso percorso file.

Questo evita la necessità di verificare se il percorso del file è disponibile in un istante o in un ciclo, secondo il commento di Chris sulla risposta di Scott Dorman.

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

  return tempFolder;
}

Se hai davvero bisogno di un nome casuale crittograficamente sicuro, potresti voler adattare la risposta di Scott per usare un ciclo while o do per continuare a provare a creare un percorso sul disco.


7

Mi piace usare GetTempPath (), una funzione di creazione GUID come CoCreateGuid () e CreateDirectory ().

Un GUID è progettato per avere un'alta probabilità di unicità ed è anche altamente improbabile che qualcuno crei manualmente una directory con la stessa forma di un GUID (e se lo fa, allora CreateDirectory () non indicherà la sua esistenza).


5

@Chris. Anch'io ero ossessionato dal rischio remoto che potesse già esistere una directory temporanea. Anche le discussioni su random e crittograficamente forti non mi soddisfano del tutto.

Il mio approccio si basa sul fatto fondamentale che l'O / S non deve consentire a 2 chiamate di creare un file per avere successo. È un po 'sorprendente che i progettisti .NET abbiano scelto di nascondere la funzionalità API Win32 per le directory, il che rende questo molto più semplice, perché restituisce un errore quando si tenta di creare una directory per la seconda volta. Ecco cosa uso:

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

Puoi decidere se vale il "costo / rischio" del codice p / invoke non gestito. La maggior parte direbbe di no, ma almeno ora hai una scelta.

CreateParentFolder () viene lasciato come esercizio allo studente. Uso Directory.CreateDirectory (). Fare attenzione a ottenere il genitore di una directory, poiché è nullo quando si trova alla radice.


5

Di solito lo uso:

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

Se si desidera essere assolutamente sicuri che questo nome di directory non esista nel percorso temporaneo, è necessario verificare se esiste questo nome di directory univoco e provare a crearne uno diverso se esiste davvero.

Ma questa implementazione basata su GUID è sufficiente. Non ho esperienza con alcun problema in questo caso. Alcune applicazioni MS utilizzano anche directory temporanee basate su GUID.


1

GetTempPath è il modo corretto di farlo; Non sono sicuro di quale sia la tua preoccupazione per questo metodo. È quindi possibile utilizzare CreateDirectory per farlo.


Un problema è che GetTempFileName creerà un file a zero byte. È invece necessario utilizzare GetTempPath, GetRandomFileName e CreateDirectory.
Scott Dorman,

Che va bene, possibile e fattibile. Stavo per fornire il codice, ma Dorman l'ha preso prima di me e funziona correttamente.

1

Ecco un approccio un po 'più bruto per risolvere il problema di collisione per nomi di directory temporanei. Non è un approccio infallibile, ma riduce significativamente le possibilità di collisione del percorso di una cartella.

Si potrebbe potenzialmente aggiungere altre informazioni relative al processo o all'assemblaggio al nome della directory per rendere la collisione ancora meno probabile, sebbene rendere tale informazione visibile sul nome della directory temporanea potrebbe non essere desiderabile. Si potrebbe anche mescolare l'ordine con cui i campi relativi al tempo vengono combinati per rendere i nomi delle cartelle più casuali. Personalmente preferisco lasciarlo così semplicemente perché è più facile per me trovarli tutti durante il debug.

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

Come accennato in precedenza, Path.GetTempPath () è un modo per farlo. È inoltre possibile chiamare Environment.GetEnvironmentVariable ("TEMP") se l'utente ha impostato una variabile d'ambiente TEMP.

Se stai pianificando di utilizzare la directory temporanea come mezzo per conservare i dati nell'applicazione, probabilmente dovresti considerare l'utilizzo di IsolatedStorage come repository per configurazione / stato / ecc ...

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.