web-dev-qa-db-fra.com

Comment vérifier facilement si l'accès est refusé à un fichier dans .NET?

En gros, j'aimerais vérifier si j'ai le droit d'ouvrir le fichier avant d'essayer de l'ouvrir; Je ne veux pas utiliser un essai/attraper pour cette vérification, sauf si je dois le faire. Existe-t-il une propriété d'accès aux fichiers que je peux vérifier avant de commencer?

98
Horas

J'ai fait cela d'innombrables fois dans le passé, et presque chaque fois que je l'ai fait, je me suis trompé.

Les autorisations de fichiers (même l'existence de fichiers) sont volatiles - elles peuvent changer à tout moment. Grâce à la loi de Murphy, cette en particulier inclut la brève période entre le moment où vous vérifiez le fichier et celle où vous essayez de l'ouvrir. Un changement est encore plus probable si vous vous trouvez dans une région où vous savez que vous devez d'abord vérifier. Pourtant, étrangement, cela ne se produira jamais dans vos environnements de test ou de développement, qui ont tendance à être assez statiques. Cela rend le problème difficile à dépister plus tard et permet à ce type de bogue de le transformer en production.

Cela signifie que vous devez toujours pouvoir gérer l'exception si les autorisations ou l'existence du fichier sont incorrectes, malgré votre vérification. Le code de traitement des exceptions est obligatoire, que vous vérifiiez ou non les autorisations du fichier à l'avance. Le code de traitement des exceptions fournit tout à la fonctionnalité de vérification de l'existence ou des permissions. De plus, bien que les gestionnaires d’exception comme celui-ci soient connus pour être lents, il est important de se rappeler que les entrées/sorties sur disque sont encore plus lentes ... a lot plus lent ... et en appelant la fonction .Exists () ou le fait de vérifier les autorisations forcera une sortie supplémentaire du système de fichiers.

En résumé, une vérification initiale avant d'essayer d'ouvrir le fichier est à la fois redondante et inutile. La gestion des exceptions n'apporte aucun avantage supplémentaire. En réalité, votre performance ne sera pas gênante, elle augmentera le coût en termes de plus de code à maintenir, et elle pourra introduire des bogues subtils dans votre code. Il n’ya aucun avantage à effectuer la vérification initiale. Au lieu de cela, la bonne chose à faire ici est simplement d’essayer d’ouvrir le fichier et de placer vos efforts dans un bon gestionnaire d’exceptions s’il échoue. La même chose est vraie même si vous ne faites que vérifier si le fichier existe ou non. Ce raisonnement s’applique à toute ressource volatile.

151
Joel Coehoorn

Astuce pour ceux qui viennent ici avec un problème similaire:

Méfiez-vous des applications de synchronisation Web telles que DropBox. Je viens de passer 2 heures à penser que la déclaration "using" (motif Dispose) est rompue dans .NET.

J'ai finalement réalisé que Dropbox lisait et écrivait continuellement des fichiers en arrière-plan, afin de les synchroniser.

Devinez où se trouve mon dossier Projets Visual Studio? Dans le dossier "My Dropbox" bien sûr.

C'est pourquoi, lorsque j'ai exécuté mon application en mode débogage, les fichiers qu'elle lisait et écrivait étaient également consultés en permanence par DropBox pour être synchronisés avec le serveur DropBox. Cela a provoqué des conflits de verrouillage/accès.

Donc, au moins, je sais maintenant que je dois disposer d’une fonction File Open plus robuste (c’est-à-dire TryOpen () qui effectuera plusieurs tentatives). Je suis surpris que ce ne soit pas déjà une partie intégrante du cadre.

[Mise à jour]

Voici ma fonction d'assistance:

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

Voici la solution que vous recherchez

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

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

cela crée une nouvelle autorisation de lecture basée sur la vue pour le chemin de tous les fichiers, puis vérifie si elle est égale à l'accès aux fichiers en lecture.

4
dj shahar

Tout d'abord, ce que Joel Coehoorn a dit.

De plus, vous devriez examiner les hypothèses qui sous-tendent votre désir d'éviter d'utiliser try/catch sauf si vous devez le faire. La raison typique d'éviter la logique qui dépend d'exceptions (la création de Exception objets fonctionne mal) n'est probablement pas pertinente pour le code qui ouvre un fichier.

Je suppose que si vous écrivez une méthode qui remplit un List<FileStream> en ouvrant tous les fichiers d'une sous-arborescence de répertoires et si vous vous attendiez à ce qu'ils soient inaccessibles, vous souhaiterez peut-être vérifier les autorisations sur les fichiers avant d'essayer d'ouvrir un fichier afin d'éviter de trop nombreuses exceptions. Mais vous géreriez toujours l'exception. En outre, il y a probablement quelque chose qui cloche dans la conception de votre programme si vous écrivez une méthode qui le fait.

3
Robert Rossney
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;
        }
0
Omid Maturi