Windows'ta geçici bir dizin mi oluşturuyorsunuz?


126

Windows'ta geçici dizin adı almanın en iyi yolu nedir? Geçici bir dosya kullanabileceğimi GetTempPathve GetTempFileNameoluşturabileceğimi görüyorum , ancak mkdtempgeçici bir dizin oluşturmak için Linux / BSD işlevinin herhangi bir eşdeğeri var mı?


Bu soruyu bulmak biraz zor görünüyor. Özellikle, Stack Overflow arama kutusuna "temp directory .net" gibi şeyler yazarsanız görünmez. Bu talihsiz görünüyor, çünkü cevaplar şimdiye kadar tüm .NET cevapları. ".Net" etiketini ekleyebileceğinizi düşünüyor musunuz? (Ve belki "dizin" veya "geçici dizin" etiketi?) Ya da belki başlığa ".NET" kelimesini ekleyin? Belki de soru gövdesinde "geçici" ile "geçici" kelimesini alternatif olarak söyleyin - bu nedenle, daha kısa biçimi ararsanız, yine de iyi bir metin arama eşleşmesi elde edersiniz. Sanırım bunları kendim yapacak kadar itibarım yok. Teşekkürler.
Chris

NET olmayan bir cevap arıyordum, bu yüzden bunu dışarıda bırakmayı tercih ederim, ancak önerdiğiniz diğer düzenlemeleri yaptım. Teşekkürler.
Josh Kelley

@Josh Kelley: Win32 API'yi iki kez kontrol ettim ve mevcut olan tek seçenek geçici yolu elde etmek, bir ramdom dosya adı oluşturmak ve ardından bir dizin oluşturmak gibi benzer bir yaklaşımı takip etmek.
Scott Dorman

Yanıtlar:


230

Hayır, mkdtemp'in eşdeğeri yoktur. En iyi seçenek bir arada kullanmaktır GetTempPath ve GetRandomFileName .

Şuna benzer bir koda ihtiyacınız olacak:

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

3
Bu biraz tehlikeli görünüyor. Özellikle, Path.Combine (Path.GetTempPath (), Path.GetRandomFileName ()), zaten var olan bir dizinin adını döndürme şansı (küçük, ancak sıfır olmayan, değil mi?). Directory.CreateDirectory (tempDirectory), tempDirectory zaten mevcutsa bir istisna atmayacağından, bu durum uygulamanız tarafından algılanmayacaktır. Ve sonra birbirinizin çalışmasına basan iki uygulamanız olabilir. .NET'te daha güvenli bir alternatif var mı?
Chris

22
@Chris: GetRandomFileName yöntemi, bir klasör adı veya dosya adı olarak kullanılabilen şifreleme açısından güçlü, rastgele bir dize döndürür. Sanırım ortaya çıkan yolun zaten var olması teorik olarak mümkün, ancak bunu yapmanın başka yolu yok. Yolun var olup olmadığını ve Path.GetRandomFileName () 'i tekrar çağırıp çağırmadığını kontrol edebilir ve tekrarlayabilirsiniz.
Scott Dorman

5
@Chris: Evet, güvenlik konusunda bu kadar endişeliyseniz , Path.Combine ve Directory.CreateDirectory çağrıları arasında başka bir işlemin dizini yaratma ihtimali çok düşüktür .
Scott Dorman

8
GetRandomFileName, 11 rastgele küçük harf ve sayı üretir; bu, etki alanı boyutunun (26 + 10) ^ 11 = ~ 57 bit olduğu anlamına gelir. Karar vermek için her zaman iki arama yapabilirsiniz
Marty Neal

27
Dizinin var olmadığını kontrol ettikten hemen sonra, tanrı evreni duraklatabilir, gizlice girebilir ve yazmak üzere olduğunuz tüm dosyalarla aynı dizini oluşturabilir ve uygulamanızın patlamasına neden olarak gününüzü mahvedebilir.
Triynko

25

Ben kesmek Path.GetTempFileName(), sonra bana diskte geçerli, sözde rasgele dosyayolu vermek dosyasını silin ve aynı dosya yolu ile bir dizin oluşturun.

Bu, Chris'in Scott Dorman'ın cevabı hakkındaki yorumuna göre, dosya yolunun bir süre veya döngü içinde kullanılabilir olup olmadığını kontrol etme ihtiyacını ortadan kaldırır.

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

  return tempFolder;
}

Kriptografik olarak güvenli bir rastgele isme gerçekten ihtiyacınız varsa, Scott'ın yanıtını diskte bir yol oluşturmaya devam etmek için bir süre veya do döngüsünü kullanmaya uyarlamak isteyebilirsiniz.


7

CoCreateGuid () ve CreateDirectory () gibi bir GUID oluşturma işlevi olan GetTempPath () kullanmayı seviyorum.

Bir GUID, yüksek bir benzersizlik olasılığına sahip olacak şekilde tasarlanmıştır ve aynı zamanda, bir kişinin bir GUID ile aynı biçime sahip bir dizini manuel olarak oluşturması da son derece olası değildir (ve eğer yaparsa, CreateDirectory (), varlığını göstererek başarısız olur.)


5

@Chris. Ben de geçici bir dizinin halihazırda mevcut olma riskine kafayı takmıştım. Rastgele ve kriptografik açıdan güçlü tartışmalar da beni tamamen tatmin etmiyor.

Benim yaklaşımım, O / S'nin 2 çağrının her ikisinin de başarılı olması için bir dosya oluşturmasına izin vermemesi gerektiği gerçeğine dayanıyor. NET tasarımcılarının dizinler için Win32 API işlevini gizlemeyi seçmeleri biraz şaşırtıcıdır, bu da bunu çok daha kolaylaştırır, çünkü ikinci kez bir dizin oluşturmaya çalıştığınızda bir hata döndürür. İşte kullandığım şey:

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

Yönetilmeyen p / invoke kodunun "maliyetinin / riskinin" buna değip değmeyeceğine siz karar verirsiniz. Çoğu kişi öyle olmadığını söyler, ama en azından şimdi bir seçeneğiniz var.

CreateParentFolder () öğrenciye alıştırma olarak bırakılır. Directory.CreateDirectory () kullanıyorum. Kökteyken boş olduğu için bir dizinin üstünü alırken dikkatli olun.


5

Genellikle bunu kullanırım:

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

Bu dizin adının geçici yolda bulunmayacağından kesinlikle emin olmak istiyorsanız, bu benzersiz dizin adının var olup olmadığını kontrol etmeniz ve gerçekten varsa başka bir tane oluşturmaya çalışmanız gerekir.

Ancak bu GUID tabanlı uygulama yeterlidir. Bu durumda herhangi bir sorunla ilgili deneyimim yok. Bazı MS uygulamaları da GUID tabanlı geçici dizinler kullanır.


1

GetTempPath bunu yapmanın doğru yoludur; Bu yöntemle ilgili endişenizin ne olduğundan emin değilim. Bunu yapmak için CreateDirectory'yi kullanabilirsiniz .


GetTempFileName'in sıfır baytlık bir dosya oluşturması bir problemdir. Bunun yerine GetTempPath, GetRandomFileName ve CreateDirectory kullanmanız gerekir.
Scott Dorman

Hangisi iyi, mümkün ve yapılabilir. Kod verecektim ama Dorman benden önce aldı ve doğru çalışıyor.

1

İşte geçici dizin adları için çakışma sorununu çözmek için biraz daha kaba kuvvet yaklaşımı. Bu, hatasız bir yaklaşım değildir, ancak bir klasör yolu çakışması olasılığını önemli ölçüde azaltır.

Bu tür bir bilginin geçici dizin adında görünür hale getirilmesi arzu edilmese de, potansiyel olarak diğer işlem veya derleme ile ilgili bilgileri dizin adına ekleyerek çarpışmayı daha da azaltabilir. Klasör adlarının daha rastgele görünmesi için zamanla ilgili alanların birleştirildiği sıra da karıştırılabilir. Ben şahsen bu şekilde bırakmayı tercih ediyorum çünkü hata ayıklama sırasında hepsini bulmam daha kolay.

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

Yukarıda belirtildiği gibi, Path.GetTempPath () bunu yapmanın bir yoludur. Kullanıcının bir TEMP ortam değişkeni ayarlaması varsa Environment.GetEnvironmentVariable ("TEMP") de çağırabilirsiniz .

Geçici dizini uygulamada veri kalıcılığı için bir araç olarak kullanmayı planlıyorsanız, muhtemelen IsolatedStorage'ı yapılandırma / durum / vb. İçin bir depo olarak kullanmalısınız .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.