як можна легко перевірити, чи заборонено доступ до файлу в .NET?


100

В основному, я хотів би перевірити, чи маю я права відкривати файл, перш ніж я спробую його відкрити; Я не хочу використовувати пробу / лову для цієї перевірки, якщо мені не доведеться. Чи є властивість доступу до файлів, яку я можу перевірити перед рукою?


2
Заголовок, коли я змінив тег: "IM коректую". Без жартів.
Джоел Куехорн

6
Погоджено. Я хотів би, щоб був TryOpen (тобто шаблон Try-Parse).
Трістан

Відповіді:


157

Я робив це незліченно разів у минулому, і майже кожного разу, коли робив це, я неправильно навіть робив спробу.

Дозволи на файли (навіть існування файлу) є мінливими - вони можуть змінюватися в будь-який час. Завдяки закону Мерфі це особливо включає короткий період між перевіркою файлу та спробою відкриття. Зміна ще ймовірніша, якщо ви знаходитесь в районі, де ви знаєте, що потрібно перевірити спочатку. Однак, як не дивно, це ніколи не станеться у ваших середовищах тестування чи розробки, які, як правило, є досить статичними. Це ускладнює проблему відстеження пізніше і полегшує можливість цього типу помилок перетворити його на виробництво.

Це означає, що ви все ще повинні мати можливість обробляти виняток, якщо дозволи або існування файлів погані, незважаючи на вашу перевірку. Код обробки винятків необхідний , незалежно від того, чи заздалегідь перевіряєте дозволи на файл. Код обробки винятків забезпечує всю функціональність перевірок існування чи дозволів. Крім того, хоча відомо, що обробники винятків є повільними, важливо пам’ятати, що введення / виведення диска ще повільніше ... набагато повільніше ... та виклик функції .Exists () або перевірка дозволів змусить додаткову поїздку з файлової системи.

Підводячи підсумок, початкова перевірка перед спробою відкрити файл є і зайвою, і марною. Немає додаткової переваги над обробкою винятків, це фактично зашкодить, а не допоможе вашій продуктивності, це додасть витрат з точки зору більшого коду, який необхідно підтримувати, і це може ввести непомітні помилки у ваш код. Просто немає першої сторони, щоб зробити початкову перевірку. Натомість тут правильно - просто спробувати відкрити файл і прикласти зусилля до хорошого обробника винятків, якщо він не вдасться. Те саме відбувається, навіть якщо ви просто перевіряєте, чи існує файл. Це міркування стосується будь-якого енергонезалежного ресурсу.


5
Саме так. Це класичний приклад стану гонки.
Powerlord

3
korro: у будь-якому випадку ви повинні вміти керувати поганими дозволами на помилку, і це робить початкову перевірку зайвою та марною.
Джоель Куехорн

2
Початкова перевірка може допомогти вишукано вирішити загальні конкретні помилки - заздалегідь заглянути вперед набагато простіше, ніж відповідати окремим атрибутам винятку конкретним причинам. Спроба / улов все ще залишається обов'язковою.
peterchen

5
Ця відповідь не відповідає на запитання "як перевірити, чи маю я права відкривати файл" в якомусь випадку, перш ніж намагатися його відкрити. Випадком може бути те, що якщо дозвіл не буде дозволений у цьому випадку, програмне забезпечення не намагатиметься прочитати файл, навіть якщо дозвіл може бути наданий відразу після перевірки дозволів.
Трайнко

5
Не має значення, чи дозволи є мінливими, коли вам цікаво лише те, що вони є в той момент. Невдача повинна завжди оброблятися, але якщо ви перевірите наявність дозволу на читання, а її немає, можливо, ви захочете пропустити читання файлу, навіть якщо можливо, що через секунду у вас може з’явитися доступ. Ви повинні десь намалювати лінію.
Трайнко

25

Коротка порада для всіх, хто приходить сюди зі схожою проблемою:

Слідкуйте за додатками для веб-синхронізації, такими як DropBox. Я щойно провів 2 години, думаючи, що "використовуючи" заяву (Dispose pattern) порушено в .NET.

Зрештою я зрозумів, що Dropbox постійно читає і записує файли у фоновому режимі, щоб синхронізувати їх.

Здогадайтесь, де знаходиться моя папка Visual Studio Projects? Всередині папки "Мій Dropbox" звичайно.

Тому, коли я запускав свою програму в режимі налагодження, DropBox також постійно отримував доступ до файлів, які вона читала і записувала, щоб синхронізуватися з сервером DropBox. Це спричинило конфлікти блокування / доступу.

Так що я, принаймні, тепер знаю, що мені потрібна більш міцна функція відкриття файлів (тобто TryOpen (), яка зробить кілька спроб). Я здивований, що це вже не вбудована частина фреймворку.

[Оновлення]

Ось моя функція помічника:

/// <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
@Ash Я думаю, ви не читали питання належним чином, він хоче уникнути спробу ловити.
Равіша

10
@Ravisha, ти навіть читав відповідь, яку голосував Джоел? Як каже Джоел, "Що ви робите замість цього, просто спробуйте відкрити файл і обробити виняток, якщо він не вдається" . Будь ласка, не зволікайте лише тому, що вам не подобається той факт, що чогось неможливо уникнути.
Ясен

Дякуємо за код! Одне, що може бути краще використовувати, наприклад, дивіться відповідь Tazeem тут
Cel

З огляду на те, що ви повернете файловий потік, тоді абонент usingповинен був би використовуватись, хоча ...
Сел.

@Cel - usingтут не працюватиме. Після закінчення використання блоку fsбуде примусово закрито. Ви дасте абоненту ЗАКРИТИЙ (настільки марний) потік файлів!
ToolmakerSteve

4

Ось рішення, яке ви шукаєте

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

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

при цьому створюється новий дозвіл на читання на основі перегляду для шляху всіх файлів, а потім перевіряється, чи він рівний доступу до файлу читання.


3

По-перше, що сказав Джоел Куехорн.

Також: слід вивчити припущення, які лежать в основі вашого бажання уникати використання спробувати / ловити, якщо не потрібно. Типова причина уникнення логіки, яка залежить від винятків (створення Exceptionоб'єктів працює погано), ймовірно, не стосується коду, який відкриває файл.

Я припускаю, що якщо ви пишете метод, який заповнює a List<FileStream>, відкриваючи кожен файл у піддерево каталога, і ви очікували, що велика кількість з них буде недоступною, ви можете перевірити дозволи файлів, перш ніж намагатися відкрити файл, щоб у вас не було отримати занадто багато винятків. Але ви все одно поправитеся з винятком. Також, мабуть, щось страшно не так у дизайні вашої програми, якщо ви пишете метод, який це робить.


-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: використовувати "кинути;" не "кидати несанкціонованіAccessException;". Ви втрачаєте свій слід стека.
Джон Сондерс

Чому attemptsпередається посилання? В цьому немає сенсу. Ні тестування, <=а не просто ==.
Конрад Рудольф

1
@John: ну в цьому випадку бажано втратити (глибоко вкладений) слід стека рекурсивного виклику, тому я думаю, що в цьому випадку throw exнасправді це правильно зробити.
Конрад Рудольф

2
@Konrad: @Rudzitis: Я змінюю причину -1. Це гірше, ніж накручувати стек "кинути екс". Ви накручуєте стек, штучно індукуючи додаткові рівні стеку за допомогою рекурсії в той момент, коли глибина стека насправді має значення. Це ітеративна проблема, а не рекурсивна.
Джон Сондерс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.