Как вы можете легко проверить, запрещен ли доступ к файлу в .NET?

В принципе, я хотел бы проверить, есть ли у меня права на открытие файла, прежде чем я попытаюсь его открыть; Я не хочу использовать try/catch для этой проверки, если я не обязан. Есть ли свойство доступа к файлу, которое я могу проверить перед началом работы?

Ответ 1

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

Разрешения на файлы (даже существование файла) являются volatile — они могут измениться в любое время. Благодаря Murphy Law это особенно касается краткого периода между проверкой файла и попыткой его открыть. Изменение даже более вероятно, если вы находитесь в районе, где вы знаете, что вам нужно сначала проверить. Однако, как ни странно, этого никогда не случится в вашей среде тестирования или разработки, которая, как правило, довольно статична. Это затрудняет отслеживание проблемы и облегчает ее создание.

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

Таким образом, первоначальная проверка перед попыткой открыть файл является избыточной и расточительной. Дополнительной выгоды от обработки исключений не существует, на самом деле это будет больно, а не помогает, ваша производительность, это добавляет стоимость с точки зрения большего количества кода, который должен поддерживаться, и он может вводить тонкие ошибки в ваш код. Нет ничего хорошего, чтобы выполнить первоначальную проверку. Вместо этого, правильная вещь здесь - просто попытаться открыть файл и приложить свои усилия к хорошему обработчику исключений, если он не сработает. То же самое верно, даже если вы просто проверяете, существует ли файл. Это рассуждение применимо к любому энергозависимому ресурсу.

Ответ 2

Быстрый подсказку для кого-то другого, пришедшего сюда с аналогичной проблемой:

Следите за приложениями для веб-синхронизации, такими как DropBox. Я просто потратил 2 часа, думая, что выражение "using" (Dispose pattern) нарушено в .NET.

В конце концов я понял, что Dropbox постоянно читает и записывает файлы в фоновом режиме, чтобы синхронизировать их.

Предположим, где находится папка проектов Visual Studio? Конечно, в папке "Мой Dropbox".

Поэтому, когда я запускал свое приложение в режиме отладки, файлы, которые он читал и записывал, также непрерывно обращались DropBox для синхронизации с сервером DropBox. Это вызвало конфликты блокировки/доступа.

Итак, по крайней мере, теперь я знаю, что мне нужна более надежная функция Open Open (т.е. 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

Эрик Липперт, вероятно, объясняет, почему Джоэл прав здесь

Ответ 4

Во-первых, что сказал Джоэл Кохорн.

Также: вы должны изучить предположения, что в основе вашего желания избегать использования try/catch, если вам не нужно. Типичная причина избежать логики, зависящей от исключений (создание объектов Exception выполняется плохо), вероятно, не относится к коду, открывающему файл.

Я полагаю, что если вы пишете метод, который заполняет List<FileStream>, открывая каждый файл в поддереве каталога и ожидая, что их большое количество будет недоступным, вы можете проверить права доступа к файлам, прежде чем пытаться открыть файл так что вы не получили слишком много исключений. Но вы все равно будете обрабатывать исключение. Кроме того, возможно, что-то ужасно не так с вашим дизайном программы, если вы пишете метод, который делает это.

Ответ 5

Вот решение, которое вы ищете

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

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

это создает новое разрешение чтения на основе представления для пути всех файлов, а затем проверяет, соответствует ли оно файлу доступ к файлу.

Ответ 6

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

Ответ 7

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