Ein temporäres Verzeichnis in Windows erstellen?

126

Was ist der beste Weg, um einen temporären Verzeichnisnamen in Windows zu erhalten? Ich sehe, dass ich eine temporäre Datei verwenden GetTempPathund GetTempFileNameerstellen kann. Gibt es jedoch eine Entsprechung zur Linux / BSD- mkdtempFunktion zum Erstellen eines temporären Verzeichnisses?

Josh Kelley
quelle
Diese Frage scheint etwas schwer zu finden. Insbesondere wird es nicht angezeigt, wenn Sie Dinge wie "temporäres Verzeichnis .net" in das Suchfeld "Stapelüberlauf" eingeben. Das scheint bedauerlich, da die Antworten bisher alle .NET-Antworten sind. Denken Sie, Sie könnten das Tag ".net" hinzufügen? (Und vielleicht das Tag "Verzeichnis" oder "temporäres Verzeichnis"?) Oder vielleicht das Wort ".NET" zum Titel hinzufügen? Vielleicht wechseln Sie auch im Fragenkörper abwechselnd "temp" mit "temporär" ab. Wenn Sie also nach der kürzeren Form suchen, erhalten Sie immer noch eine gute Übereinstimmung bei der Textsuche. Ich habe anscheinend nicht genug Repräsentanten, um diese Dinge selbst zu tun. Vielen Dank.
Chris
Nun, ich habe nach einer Nicht-NET-Antwort gesucht, daher würde ich das lieber weglassen, aber ich habe die anderen von Ihnen vorgeschlagenen Änderungen vorgenommen. Vielen Dank.
Josh Kelley
@ Josh Kelley: Ich habe gerade die Win32-API überprüft und die einzigen Optionen, die es gibt, sind, einen ähnlichen Ansatz zu verfolgen, den temporären Pfad abzurufen, einen Ramdom-Dateinamen zu generieren und dann ein Verzeichnis zu erstellen.
Scott Dorman

Antworten:

230

Nein, es gibt kein Äquivalent zu mkdtemp. Die beste Option ist die Verwendung einer Kombination aus GetTempPath und GetRandomFileName .

Sie benötigen einen ähnlichen Code:

public string GetTemporaryDirectory()
{
   string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
   Directory.CreateDirectory(tempDirectory);
   return tempDirectory;
}
Scott Dorman
quelle
3
Das scheint ein wenig gefährlich. Insbesondere besteht die Möglichkeit (klein, aber nicht Null, oder?), Dass Path.Combine (Path.GetTempPath (), Path.GetRandomFileName ()) den Namen eines bereits vorhandenen Verzeichnisses zurückgibt. Da Directory.CreateDirectory (tempDirectory) keine Ausnahme auslöst, wenn tempDirectory bereits vorhanden ist, wird dieser Fall von Ihrer Anwendung nicht erkannt. Und dann haben Sie möglicherweise zwei Anwendungen, die sich gegenseitig auf die Arbeit treten. Gibt es eine sicherere Alternative in .NET?
Chris
22
@Chris: Die GetRandomFileName-Methode gibt eine kryptografisch starke, zufällige Zeichenfolge zurück, die entweder als Ordnername oder als Dateiname verwendet werden kann. Ich nehme an, es ist theoretisch möglich, dass der resultierende Pfad bereits existiert, aber es gibt keine anderen Möglichkeiten, dies zu tun. Sie können überprüfen, ob der Pfad vorhanden ist und ob Path.GetRandomFileName () erneut aufgerufen wird, und wiederholen.
Scott Dorman
5
@Chris: Ja, wenn Sie sich in diesem Ausmaß Sorgen um die Sicherheit machen, besteht eine sehr geringe Wahrscheinlichkeit, dass ein anderer Prozess das Verzeichnis zwischen den Aufrufen Path.Combine und Directory.CreateDirectory erstellt.
Scott Dorman
8
GetRandomFileName generiert 11 zufällige Kleinbuchstaben und Zahlen, was bedeutet, dass die Domänengröße (26 + 10) ^ 11 = ~ 57 Bit beträgt. Sie können immer zwei Anrufe tätigen, um es
auszugleichen
27
Kurz nachdem Sie überprüft haben, dass das Verzeichnis nicht vorhanden ist, könnte Gott das Universum anhalten, sich einschleichen und dasselbe Verzeichnis mit denselben Dateien erstellen, die Sie gerade schreiben, wodurch Ihre App in die Luft sprengt und Ihren Tag ruiniert.
Triynko
25

Ich hacke Path.GetTempFileName(), um mir einen gültigen, pseudozufälligen Dateipfad auf der Festplatte zu geben, lösche dann die Datei und erstelle ein Verzeichnis mit demselben Dateipfad.

Auf diese Weise muss nicht überprüft werden, ob der Dateipfad in einer Weile oder in einer Schleife verfügbar ist. Dies geht aus Chris 'Kommentar zu Scott Dormans Antwort hervor.

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

  return tempFolder;
}

Wenn Sie wirklich einen kryptografisch sicheren Zufallsnamen benötigen, können Sie die Antwort von Scott anpassen, um eine Weile zu verwenden, oder eine Schleife ausführen, um weiterhin zu versuchen, einen Pfad auf der Festplatte zu erstellen.

Steve Jansen
quelle
7

Ich verwende gerne GetTempPath (), eine GUID-Erstellungsfunktion wie CoCreateGuid () und CreateDirectory ().

Eine GUID weist eine hohe Wahrscheinlichkeit der Eindeutigkeit auf, und es ist höchst unwahrscheinlich, dass jemand manuell ein Verzeichnis mit derselben Form wie eine GUID erstellt (und wenn dies der Fall ist, schlägt CreateDirectory () fehl, um seine Existenz anzuzeigen.)

Matthew
quelle
5

@ Chris. Auch ich war besessen von dem Risiko, dass ein temporäres Verzeichnis bereits existiert. Die Diskussionen über zufällig und kryptografisch stark befriedigen mich auch nicht ganz.

Mein Ansatz baut auf der fundamentalen Tatsache auf, dass das Betriebssystem nicht zulassen darf, dass zwei Aufrufe eine Datei erstellen, damit beide erfolgreich sind. Es ist ein wenig überraschend, dass .NET-Designer die Win32-API-Funktionalität für Verzeichnisse ausgeblendet haben, was dies erheblich vereinfacht, da beim zweiten Versuch, ein Verzeichnis zu erstellen, ein Fehler zurückgegeben wird. Folgendes benutze ich:

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

Sie können entscheiden, ob sich die "Kosten / Risiken" von nicht verwaltetem P / Invoke-Code lohnen. Die meisten würden sagen, dass dies nicht der Fall ist, aber zumindest haben Sie jetzt die Wahl.

CreateParentFolder () wird dem Schüler als Übung überlassen. Ich benutze Directory.CreateDirectory (). Achten Sie darauf, dass Sie das übergeordnete Verzeichnis eines Verzeichnisses abrufen, da es im Stammverzeichnis null ist.

Andrew Dennison
quelle
5

Ich benutze normalerweise Folgendes:

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

Wenn Sie absolut sicher sein möchten, dass dieser Verzeichnisname nicht im temporären Pfad vorhanden ist, müssen Sie überprüfen, ob dieser eindeutige Verzeichnisname vorhanden ist, und versuchen, einen anderen zu erstellen, falls er tatsächlich vorhanden ist.

Diese GUID-basierte Implementierung ist jedoch ausreichend. Ich habe in diesem Fall keine Erfahrung mit Problemen. Einige MS-Anwendungen verwenden auch GUID-basierte temporäre Verzeichnisse.

Jan Hlavsa
quelle
1

GetTempPath ist der richtige Weg, dies zu tun. Ich bin mir nicht sicher, was Sie über diese Methode besorgt sind. Sie können dann CreateDirectory verwenden , um es zu erstellen .


quelle
Ein Problem ist, dass GetTempFileName eine Null-Byte-Datei erstellt. Sie müssen stattdessen GetTempPath, GetRandomFileName und CreateDirectory verwenden.
Scott Dorman
Welches ist in Ordnung, möglich und machbar. Ich wollte Code bereitstellen, aber Dorman hat ihn vor mir und er funktioniert korrekt.
1

Hier ist ein etwas Brute-Force-Ansatz zur Lösung des Kollisionsproblems für temporäre Verzeichnisnamen. Dies ist kein unfehlbarer Ansatz, verringert jedoch die Wahrscheinlichkeit einer Kollision mit Ordnerpfaden erheblich.

Man könnte möglicherweise andere prozess- oder montagebezogene Informationen zum Verzeichnisnamen hinzufügen, um die Kollision noch weniger wahrscheinlich zu machen, obwohl es möglicherweise nicht wünschenswert ist, solche Informationen im temporären Verzeichnisnamen sichtbar zu machen. Man könnte auch die Reihenfolge mischen, in der die zeitbezogenen Felder kombiniert werden, damit die Ordnernamen zufälliger aussehen. Ich persönlich ziehe es vor, es einfach so zu lassen, weil es für mich einfacher ist, sie alle beim Debuggen zu finden.

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

Wie oben erwähnt, ist Path.GetTempPath () eine Möglichkeit, dies zu tun. Sie können auch Environment.GetEnvironmentVariable ("TEMP") aufrufen, wenn der Benutzer eine TEMP-Umgebungsvariable eingerichtet hat.

Wenn Sie vorhaben, das temporäre Verzeichnis als Mittel zum Speichern von Daten in der Anwendung zu verwenden, sollten Sie wahrscheinlich IsolatedStorage als Repository für Konfiguration / Status / usw. verwenden.

Chris Rauber
quelle