Como evitar a Java FileNotFoundException ao carregar recursos
1. Visão geral
Neste tutorial, vamos explorar um problema que pode surgir ao ler arquivos de recursos em um aplicativo Java: em tempo de execução, a pasta de recursos raramente está no mesmo local no disco que está em nosso código-fonte.
Vamos ver como o Java nos permite acessar arquivos de recursos depois que nosso código foi empacotado.
2. Lendo arquivos
Digamos que nosso aplicativo leia um arquivo durante a inicialização:
try (FileReader fileReader = new FileReader("src/main/resources/input.txt");
BufferedReader reader = new BufferedReader(fileReader)) {
String contents = reader.lines()
.collect(Collectors.joining(System.lineSeparator()));
}
Se executarmos o código acima em um IDE, o arquivo será carregado sem erro. Isso ocorre porqueour IDE uses our project directory as its current working directorye o diretóriosrc/main/resources está ali para o aplicativo ler.
Agora, digamos que usamosMaven JAR plugin para empacotar nosso código como um JAR.
Quando o executamos na linha de comando:
java -jar core-java-io2.jar
Veremos o seguinte erro:
Exception in thread "main" java.io.FileNotFoundException:
src/main/resources/input.txt (No such file or directory)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.(FileInputStream.java:138)
at java.io.FileInputStream.(FileInputStream.java:93)
at java.io.FileReader.(FileReader.java:58)
at com.example.resource.MyResourceLoader.loadResourceWithReader(MyResourceLoader.java:14)
at com.example.resource.MyResourceLoader.main(MyResourceLoader.java:37)
3. Código Fonte vs Código Compilado
Quando construímos um JAR, os recursos são colocados no diretório raiz dos artefatos empacotados.
Em nosso exemplo, vemos que a configuração do código-fonte teminput.txt emsrc/main/resources em nosso diretório de código-fonte.
Na estrutura JAR correspondente, no entanto, vemos:
META-INF/MANIFEST.MF META-INF/ com/ com/example/ com/example/resource/ META-INF/maven/ META-INF/maven/com.example/ META-INF/maven/com.example/core-java-io-files/ input.txt com/example/resource/MyResourceLoader.class META-INF/maven/com.example/core-java-io-files/pom.xml META-INF/maven/com.example/core-java-io-files/pom.properties
Aqui,input.txt está no diretório raiz do JAR. Então, quando o código for executado, veremosFileNotFoundException.
Mesmo se alterássemos o caminho para/input.txt, o código original não poderia carregar este arquivo comoresources are not usually addressable as files on disk. Os arquivos de recursos são empacotados dentro do JAR e, portanto, precisamos de uma maneira diferente de acessá-los.
4. Recursos
Em vez disso, vamos usar o carregamento de recursos paraload resources from the classpath em vez de um local de arquivo específico. Isso funcionará independentemente de como o código é empacotado:
try (InputStream inputStream = getClass().getResourceAsStream("/input.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
String contents = reader.lines()
.collect(Collectors.joining(System.lineSeparator()));
}
ClassLoader.getResourceAsStream() olha o classpath para o recurso fornecido. A barra inicial na entrada paragetResourceAsStream() diz ao carregador para ler a partir da base do classpath. The contents of our JAR file are on the classpath, então esse método funciona.
An IDE typically includes src/main/resources on its classpath e, assim, encontra os arquivos.
5. Conclusão
Neste artigo, implementamos o carregamento de arquivos como recursos do caminho de classe, para permitir que nosso código funcione de maneira consistente, independentemente de como foi empacotado.
Como sempre, o código de exemplo está disponívelover on GitHub.