.NET'teki bir dosya için erişimin reddedilip reddedilmediğini nasıl kolayca kontrol edebilirsiniz?


100

Temel olarak, gerçekten açmaya çalışmadan önce dosyayı açma hakkım olup olmadığını kontrol etmek istiyorum; Mecbur kalmadıkça bu çek için bir dene / yakala kullanmak istemiyorum. Önceden kontrol edebileceğim bir dosya erişim özelliği var mı?


2
Etiketi değiştirdiğimde başlık: "düzeltiyorum". Şaka değil.
Joel Coehoorn

6
Kabul - Bir TryOpen (yani Try-Ayrıştırma kalıbı) olmasını isterdim.
Tristan

Yanıtlar:


157

Bunu geçmişte sayısız kez yaptım ve neredeyse her yaptığımda, bu denemek bile hatalıydı.

Dosya izinleri (dosya varlığı bile) uçucudur - her an değişebilir. Murphy Yasası sayesinde bu, özellikle dosyayı kontrol etmeniz ve açmaya çalışmanız arasındaki kısa dönemi içerir. Önce kontrol etmeniz gerektiğini bildiğiniz bir bölgedeyseniz, değişiklik daha da olasıdır. Yine de garip bir şekilde, oldukça statik olma eğiliminde olan test veya geliştirme ortamlarınızda asla olmayacak. Bu, sorunun daha sonra izini sürmeyi zorlaştırır ve bu tür hataların üretime geçmesini kolaylaştırır.

Bunun anlamı, kontrolünüze rağmen dosya izinleri veya varlığı kötüyse, istisnayı hala idare edebilmeniz gerektiğidir. Dosyanın izinlerini önceden kontrol etseniz de etmeseniz de, istisna işleme kodu gereklidir . İstisna işleme kodu, varlığın veya izin kontrollerinin tüm işlevselliğini sağlar . Ek olarak, bunun gibi istisna işleyicilerin yavaş olduğu biliniyor olsa da, disk giriş / çıkışının daha da yavaş olduğunu ... çok daha yavaş olduğunu ve .Exists () işlevini çağırmak veya izinleri kontrol etmek ek bir geziye zorlayacaktır. dosya sistemi dışında.

Özetle, dosyayı açmaya çalışmadan önce yapılan ilk kontrol hem gereksiz hem de israftır. İstisna işlemeye göre ek bir faydası yoktur, performansınıza gerçekten zarar verir, yardımcı olmaz, korunması gereken daha fazla kod açısından maliyet ekler ve kodunuza ince hatalar ekleyebilir. İlk kontrolü yapmanın hiçbir avantajı yok. Bunun yerine, buradaki doğru şey, dosyayı açmaya çalışmak ve başarısız olursa iyi bir istisna işleyiciye çabalamaktır. Aynı şey, dosyanın var olup olmadığını kontrol etseniz bile geçerlidir. Bu muhakeme, herhangi bir geçici kaynak için geçerlidir .


5
Kesinlikle. Bu, yarış durumunun klasik bir örneğidir.
Powerlord

3
korro: yine de başarısızlık durumunda kötü izinleri idare edebilmelisiniz ve bu da ilk denetimi gereksiz ve israflı hale getirir.
Joel Coehoorn

2
İlk kontrol, sık karşılaşılan belirli hataların düzgün bir şekilde ele alınmasına yardımcı olabilir - ileriye bakmak, genellikle belirli istisna niteliklerini belirli nedenlerle eşleştirmekten daha kolaydır. Try / catch hala zorunludur.
peterchen

5
Bu yanıt, bir durumda dosyayı açmaya çalışmadan önce "dosyayı açma hakkım olup olmadığını nasıl kontrol edebilirim" sorusuna yanıt vermez. Durum çok iyi olabilir, bu durumda izin verilmezse, izinler kontrol edildikten hemen sonra izin çok iyi verilse bile yazılım dosyayı okumaya çalışmayacaktır.
Triynko

5
İzinlerin geçici olup olmadığı önemli değil, yalnızca o anda ne olduklarını önemsediğinizde. Başarısızlık her zaman ele alınmalıdır, ancak bir okuma izni olup olmadığını kontrol ederseniz ve orada değilse, bir saniye sonra erişiminizin olması mümkün olsa bile, dosyayı okumayı atlamak isteyebilirsiniz. Çizgiyi bir yere çekmelisin.
Triynko

25

Buraya benzer bir sorunla gelen herkes için hızlı ipucu:

DropBox gibi web senkronizasyon uygulamalarına dikkat edin. .NET'te "using" ifadesinin (Dispose pattern) bozuk olduğunu düşünerek 2 saat geçirdim.

Sonunda Dropbox'ın senkronize etmek için dosyaları arka planda sürekli olarak okuduğunu ve yazdığını fark ettim.

Bil bakalım Visual Studio Projeleri klasörüm nerede bulunuyor? Elbette "Dropbox'ım" klasörünün içinde.

Bu nedenle, uygulamamı Debug modunda çalıştırdığım için, okuduğu ve yazdığı dosyalara DropBox sunucusuyla senkronize edilmek üzere DropBox tarafından sürekli olarak erişiliyordu. Bu, kilitleme / erişim çatışmalarına neden oldu.

Bu yüzden en azından artık daha sağlam bir Dosya Açma fonksiyonuna ihtiyacım olduğunu biliyorum (yani, birden fazla deneme yapacak olan TryOpen ()). Çerçevenin zaten yerleşik bir parçası olmamasına şaşırdım.

[Güncelleme]

İşte benim yardımcı işlevim:

/// <summary>
/// Tries to open a file, with a user defined number of attempt and Sleep delay between attempts.
/// </summary>
/// <param name="filePath">The full file path to be opened</param>
/// <param name="fileMode">Required file mode enum value(see MSDN documentation)</param>
/// <param name="fileAccess">Required file access enum value(see MSDN documentation)</param>
/// <param name="fileShare">Required file share enum value(see MSDN documentation)</param>
/// <param name="maximumAttempts">The total number of attempts to make (multiply by attemptWaitMS for the maximum time the function with Try opening the file)</param>
/// <param name="attemptWaitMS">The delay in Milliseconds between each attempt.</param>
/// <returns>A valid FileStream object for the opened file, or null if the File could not be opened after the required attempts</returns>
public FileStream TryOpen(string filePath, FileMode fileMode, FileAccess fileAccess,FileShare fileShare,int maximumAttempts,int attemptWaitMS)
{
    FileStream fs = null;
    int attempts = 0;

    // Loop allow multiple attempts
    while (true)
    {
        try
        {
            fs = File.Open(filePath, fileMode, fileAccess, fileShare);

            //If we get here, the File.Open succeeded, so break out of the loop and return the FileStream
            break;
        }
        catch (IOException ioEx)
        {
            // IOExcception is thrown if the file is in use by another process.

            // Check the numbere of attempts to ensure no infinite loop
            attempts++;
            if (attempts > maximumAttempts)
            {
                // Too many attempts,cannot Open File, break and return null 
                fs = null;
                break;
            }
            else
            {
                // Sleep before making another attempt
                Thread.Sleep(attemptWaitMS);

            }

        }

    }
    // Reutn the filestream, may be valid or null
    return fs;
}

3
@ Bence soruyu doğru bir şekilde okumadınız HE, yakalamaktan kaçınmak istiyor.
Ravisha

10
@Ravisha, Joel'in en çok oylanan cevabını okudun mu? Joel'in dediği gibi, "Bunun yerine yaptığınız şey, dosyayı açmaya ve başarısız olursa istisnayı halletmeye çalışmaktır" . Lütfen bir şeylerin önlenemeyeceği gerçeğini beğenmediğiniz için olumsuz oy vermeyin.
Ash

Kod için teşekkürler! Bir şey, kullanmak daha iyi olabilir, örneğin Tazeem'in cevabına buradan bakın
Cel

Filestream geri döndüğünüzde, usingarayan tarafından kullanılmalıdır ...
Cel

@Cel - usingburada çalışmayacak. Kullanım bloğunun sonunda fszorla kapatılacaktır. Arayan kişiye KAPALI (çok işe yaramaz) bir filestream vereceksiniz!
ToolmakerSteve

4

İşte aradığınız çözüm

var fileIOPermission = new FileIOPermission(FileIOPermissionAccess.Read,
                                            System.Security.AccessControl.AccessControlActions.View,
                                            MyPath);

if (fileIOPermission.AllFiles == FileIOPermissionAccess.Read)
{
    // Do your thing here...
}

bu, tüm dosyaların yolunun görünümüne dayalı olarak yeni bir okuma izni oluşturur ve ardından okunan dosya erişimine eşit olup olmadığını kontrol eder.


3

Önce Joel Coehoorn'un söylediği.

Ayrıca, mecbur kalmadıkça dene / yakala'yı kullanmaktan kaçınma arzunuzun altında yatan varsayımları da incelemelisiniz. İstisnalara bağlı olan mantıktan kaçınmanın tipik nedeni ( Exceptionnesnelerin oluşturulması kötü performans gösterir) muhtemelen bir dosyayı açan kodla ilgili değildir.

Sanırım List<FileStream>bir dizin alt ağacındaki her dosyayı açarak a'yı dolduran bir yöntem yazıyorsanız ve çok sayıda kişinin erişilemez olmasını bekliyorsanız, bir dosyayı açmaya çalışmadan önce dosya izinlerini kontrol etmek isteyebilirsiniz, böylece çok fazla istisna alın. Ama yine de istisnayı halledersiniz. Ayrıca, bunu yapan bir yöntem yazıyorsanız, muhtemelen programınızın tasarımında çok büyük bir yanlışlık vardır.


-1
public static bool IsFileLocked(string filename)
        {
            bool Locked = false;
            try
            {
                FileStream fs =
                    File.Open(filename, FileMode.OpenOrCreate,
                    FileAccess.ReadWrite, FileShare.None);
                fs.Close();
            }
            catch (IOException ex)
            {
                Locked = true;
            }
            return Locked;
        }

-3
public static FileStream GetFileStream(String filePath, FileMode fileMode, FileAccess fileAccess, FileShare fileShare, ref int attempts, int attemptWaitInMilliseconds)
{            
    try
    {
         return File.Open(filePath, fileMode, fileAccess, fileShare);
    }
    catch (UnauthorizedAccessException unauthorizedAccessException)
    {
        if (attempts <= 0)
        {
            throw unauthorizedAccessException;
        }
        else
        {
            Thread.Sleep(attemptWaitInMilliseconds);
            attempts--;
            return GetFileStream(filePath, fileMode, fileAccess, fileShare, ref attempts, attemptWaitInMilliseconds);
        }
    }
}

8
-1: "throw;" kullanın "unauthorizedAccessException atmayın;". Yığın izini kaybediyorsun.
John Saunders

attemptsRef tarafından neden geçilir? Bu hiç mantıklı değil. <=Sadece yerine test etmek de yapmaz ==.
Konrad Rudolph

1
@John: peki, bu durumda özyinelemeli çağrının (derinlemesine iç içe geçmiş) yığın izini kaybetmek istenir , bu yüzden bu durumda throw exaslında yapılacak doğru şey olduğunu düşünüyorum.
Konrad Rudolph

2
@Konrad: @Rudzitis: -1 için nedenimi değiştiriyorum. "Eski fırlat" ile yığını mahvetmekten daha kötü. Yığın derinliğinin gerçekten önemli olduğu bir zamanda özyineleme yoluyla yapay olarak fazladan yığın seviyeleri oluşturarak yığını mahvediyorsunuz. Bu yinelemeli bir sorundur, özyinelemeli değil.
John Saunders
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.