Java Language Piège: tester un fichier avant d'essayer de l'ouvrir.


Exemple

Certaines personnes recommandent d’appliquer divers tests à un fichier avant de tenter de l’ouvrir pour fournir de meilleurs diagnostics ou pour éviter de traiter des exceptions. Par exemple, cette méthode tente de vérifier si le path correspond à un fichier lisible:

public static File getValidatedFile(String path) throws IOException {
    File f = new File(path);
    if (!f.exists()) throw new IOException("Error: not found: " + path);
    if (!f.isFile()) throw new IOException("Error: Is a directory: " + path);
    if (!f.canRead()) throw new IOException("Error: cannot read file: " + path);
    return f;
}

Vous pourriez utiliser la méthode ci-dessus comme ceci:

File f = null;
try {
    f = getValidatedFile("somefile");
} catch (IOException ex) {
    System.err.println(ex.getMessage());
    return;
}
try (InputStream is = new FileInputStream(file)) {
    // Read data etc.
}

Le premier problème réside dans la signature de FileInputStream(File) car le compilateur insistera toujours pour intercepter IOException ici ou plus haut dans la pile.

Le second problème est que les vérifications effectuées par getValidatedFile ne garantissent pas la FileInputStream .

  • Conditions de course: un autre thread ou un processus séparé peut renommer le fichier, supprimer le fichier ou supprimer l'accès en lecture après le retour de getValidatedFile . Cela conduirait à une IOException "simple" sans le message personnalisé.

  • Il existe des cas marginaux non couverts par ces tests. Par exemple, sur un système avec SELinux en mode "Forçage", une tentative de lecture d'un fichier peut échouer malgré le retour de true canRead() .

Le troisième problème est que les tests sont inefficaces. Par exemple, le exists , isFile et canRead appels feront chacun un syscall pour effectuer le contrôle nécessaire. Un autre appel système est alors effectué pour ouvrir le fichier, qui répète les mêmes vérifications dans les coulisses.

En bref, les méthodes telles que getValidatedFile sont erronées. Il est préférable d'essayer d'ouvrir le fichier et de gérer l'exception:

try (InputStream is = new FileInputStream("somefile")) {
    // Read data etc.
} catch (IOException ex) {
    System.err.println("IO Error processing 'somefile': " + ex.getMessage());
    return;
}

Si vous voulez distinguer les erreurs IO générées lors de l'ouverture et de la lecture, vous pouvez utiliser un try / catch imbriqué. Si vous voulez produire de meilleurs diagnostics pour les échecs ouverts, vous pouvez effectuer les exists , isFile et canRead contrôles dans le gestionnaire.