Membuat direktori sementara di Windows?

126

Apa cara terbaik untuk mendapatkan nama direktori temp di Windows? Saya melihat bahwa saya dapat menggunakan GetTempPathdan GetTempFileNamemembuat file sementara, tetapi apakah ada yang setara dengan fungsi Linux / BSD mkdtempuntuk membuat direktori sementara?

Josh Kelley
sumber
Pertanyaan ini sepertinya agak sulit ditemukan. Secara khusus, ini tidak muncul jika Anda mengetik hal-hal seperti "temp directory .net" di kotak pencarian Stack Overflow. Tampaknya disayangkan, karena sejauh ini semua jawaban adalah jawaban .NET. Apakah menurut Anda Anda bisa menambahkan tag ".net"? (Dan mungkin tag "direktori" atau "direktori sementara"?) Atau mungkin menambahkan kata ".NET" ke judul? Mungkin juga di badan pertanyaan alternatif mengatakan "temp" dengan "sementara" - jadi jika Anda mencari bentuk yang lebih pendek Anda masih akan mendapatkan pencocokan pencarian teks yang bagus. Sepertinya saya tidak memiliki cukup perwakilan untuk melakukan hal-hal ini sendiri. Terima kasih.
Chris
Yah, saya sedang mencari jawaban non- NET, jadi saya lebih suka mengabaikannya, tetapi saya membuat pengeditan lain yang Anda sarankan. Terima kasih.
Josh Kelley
@ Josh Kelley: Saya baru saja memeriksa ulang Win32 API dan satu-satunya pilihan adalah mengikuti pendekatan serupa untuk mendapatkan jalur temp, menghasilkan nama file ramdom, dan kemudian membuat direktori.
Scott Dorman

Jawaban:

230

Tidak, tidak ada yang setara dengan mkdtemp. Pilihan terbaik adalah menggunakan kombinasi GetTempPath dan GetRandomFileName .

Anda akan membutuhkan kode yang mirip dengan ini:

public string GetTemporaryDirectory()
{
   string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
   Directory.CreateDirectory(tempDirectory);
   return tempDirectory;
}
Scott Dorman
sumber
3
Ini sepertinya sedikit berbahaya. Secara khusus, ada kemungkinan (kecil, tapi bukan nol, kan?) Path.Combine (Path.GetTempPath (), Path.GetRandomFileName ()) akan mengembalikan nama direktori yang sudah ada. Karena Directory.CreateDirectory (tempDirectory) tidak akan mengeluarkan pengecualian jika tempDirectory sudah ada, kasus ini tidak akan terdeteksi oleh aplikasi Anda. Dan kemudian Anda mungkin memiliki dua aplikasi yang saling menginjak pekerjaan. Apakah ada alternatif yang lebih aman di .NET?
Chris
22
@ Chris: Metode GetRandomFileName mengembalikan string acak yang kuat secara kriptografis yang dapat digunakan sebagai nama folder atau nama file. Saya kira secara teoritis mungkin bahwa jalur yang dihasilkan sudah ada, tetapi tidak ada cara lain untuk melakukan ini. Anda bisa memeriksa untuk melihat apakah jalur tersebut ada, dan apakah itu memanggil Path.GetRandomFileName () lagi, dan ulangi.
Scott Dorman
5
@Chris: Ya, jika Anda mengkhawatirkan keamanan sejauh itu, ada kemungkinan sangat kecil bahwa proses lain dapat membuat direktori antara panggilan Path.Combine dan Directory.CreateDirectory.
Scott Dorman
8
GetRandomFileName menghasilkan 11 huruf kecil dan angka acak yang berarti ukuran domainnya adalah (26 + 10) ^ 11 = ~ 57 bit. Anda selalu dapat melakukan dua panggilan untuk menyelesaikannya
Marty Neal
27
Tepat setelah Anda memeriksa bahwa direktori tersebut tidak ada, tuhan dapat menjeda alam semesta, menyelinap masuk dan membuat direktori yang sama dengan semua file yang sama yang akan Anda tulis, menyebabkan aplikasi Anda meledak, hanya untuk merusak hari Anda.
Triynko
25

Saya meretas Path.GetTempFileName()untuk memberi saya jalur file pseudo-acak yang valid pada disk, lalu menghapus file, dan membuat direktori dengan jalur file yang sama.

Ini menghindari kebutuhan untuk memeriksa apakah jalur file tersedia dalam beberapa saat atau berulang, sesuai komentar Chris atas jawaban Scott Dorman.

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

  return tempFolder;
}

Jika Anda benar-benar membutuhkan nama acak yang aman secara kriptografik, Anda mungkin ingin menyesuaikan jawaban Scott untuk menggunakan beberapa saat atau melakukan loop untuk terus mencoba membuat jalur pada disk.

Steve Jansen
sumber
7

Saya suka menggunakan GetTempPath (), fungsi pembuatan GUID seperti CoCreateGuid (), dan CreateDirectory ().

GUID dirancang untuk memiliki kemungkinan keunikan yang tinggi, dan juga sangat tidak mungkin seseorang secara manual membuat direktori dengan bentuk yang sama dengan GUID (dan jika mereka melakukannya maka CreateDirectory () akan gagal menunjukkan keberadaannya.)

Matthew
sumber
5

@Tokopedia Saya juga terobsesi dengan risiko jarak jauh bahwa direktori sementara mungkin sudah ada. Diskusi tentang acak dan kuat secara kriptografis juga tidak sepenuhnya memuaskan saya.

Pendekatan saya dibangun di atas fakta mendasar bahwa O / S tidak boleh mengizinkan 2 panggilan untuk membuat file agar keduanya berhasil. Sedikit mengherankan bahwa perancang .NET memilih untuk menyembunyikan fungsionalitas Win32 API untuk direktori, yang membuatnya lebih mudah, karena ini mengembalikan kesalahan saat Anda mencoba membuat direktori untuk kedua kalinya. Inilah yang saya gunakan:

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

Anda dapat memutuskan apakah "biaya / risiko" dari kode p / pemanggilan yang tidak dikelola sepadan. Sebagian besar akan mengatakan tidak, tetapi setidaknya Anda sekarang punya pilihan.

CreateParentFolder () ditinggalkan sebagai latihan untuk siswa. Saya menggunakan Directory.CreateDirectory (). Berhati-hatilah saat mendapatkan induk direktori, karena nilainya null saat berada di root.

Andrew Dennison
sumber
5

Saya biasanya menggunakan ini:

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

Jika Anda ingin benar-benar yakin bahwa nama direktori ini tidak akan ada di jalur sementara, Anda perlu memeriksa apakah nama direktori unik ini ada dan mencoba membuat yang lain jika benar-benar ada.

Tapi implementasi berbasis GUID ini sudah cukup. Saya tidak punya pengalaman dengan masalah apa pun dalam kasus ini. Beberapa aplikasi MS menggunakan direktori temp berbasis GUID juga.

Jan Hlavsa
sumber
1

GetTempPath adalah cara yang benar untuk melakukannya; Saya tidak yakin apa kekhawatiran Anda tentang metode ini. Anda kemudian dapat menggunakan CreateDirectory untuk membuatnya.


sumber
Satu masalah adalah GetTempFileName akan membuat file nol-byte. Anda perlu menggunakan GetTempPath, GetRandomFileName, dan CreateDirectory sebagai gantinya.
Scott Dorman
Itu bagus, mungkin, dan bisa dilakukan. Saya akan memberikan kode tetapi Dorman mendapatkannya sebelum saya, dan berfungsi dengan benar.
1

Berikut adalah pendekatan yang lebih kasar untuk menyelesaikan masalah tabrakan untuk nama direktori sementara. Ini bukan pendekatan yang sempurna, tetapi secara signifikan mengurangi kemungkinan tabrakan jalur folder.

Seseorang berpotensi menambahkan proses lain atau informasi terkait perakitan ke nama direktori untuk membuat tabrakan lebih kecil kemungkinannya, meskipun membuat informasi seperti itu terlihat pada nama direktori sementara mungkin tidak diinginkan. Anda juga dapat mencampur urutan penggabungan bidang terkait waktu untuk membuat nama folder terlihat lebih acak. Saya pribadi lebih suka membiarkannya seperti itu hanya karena lebih mudah bagi saya untuk menemukan semuanya selama debugging.

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

Seperti disebutkan di atas, Path.GetTempPath () adalah salah satu cara untuk melakukannya. Anda juga dapat memanggil Environment.GetEnvironmentVariable ("TEMP") jika pengguna telah menyiapkan variabel lingkungan TEMP.

Jika Anda berencana menggunakan direktori temp sebagai sarana untuk menyimpan data dalam aplikasi, Anda mungkin harus menggunakan IsolatedStorage sebagai repositori untuk konfigurasi / status / dll ...

Chris Rauber
sumber