Lendo um arquivo de recurso dentro do jar

Eu gostaria de ler um recurso de dentro do meu jar assim:

File file; file = new File(getClass().getResource("/file.txt").toURI()); BufferredReader reader = new BufferedReader(new FileReader(file)); //Read the file 

e funciona bem ao executá-lo no Eclipse, mas se eu exportá-lo para um jar, execute o IllegalArgumentException:

 Exception in thread "Thread-2" java.lang.IllegalArgumentException: URI is not hierarchical 

e eu realmente não sei porque, mas com alguns testes eu encontrei se eu mudar

 file = new File(getClass().getResource("/file.txt").toURI()); 

para

 file = new File(getClass().getResource("/folder/file.txt").toURI()); 

então funciona o oposto (funciona no jar, mas não no eclipse).

Estou usando o Eclipse e a pasta com meu arquivo em é uma pasta de classs.

Em vez de tentar endereçar o recurso como um arquivo, peça ao ClassLoader para retornar um InputStream para o recurso, via getResourceAsStream :

 InputStream in = getClass().getResourceAsStream("/file.txt"); BufferedReader reader = new BufferedReader(new InputStreamReader(in)); 

Contanto que o recurso file.txt esteja disponível no caminho de class, essa abordagem funcionará da mesma maneira, independentemente de o recurso file.txt estar em um diretório classs/ ou dentro de um jar .

O URI is not hierarchical porque o URI de um recurso dentro de um arquivo jar será parecido com este: file:/example.jar!/file.txt . Você não pode ler as inputs dentro de um jar (um arquivo zip ) como se fosse um arquivo antigo.

Isso é explicado bem pelas respostas para:

  • Como faço para ler um arquivo de resources de um arquivo jar do Java?
  • Arquivo Java jar: use erros de resources: o URI não é hierárquico

Para acessar um arquivo em um jar você tem duas opções:

  • Coloque o arquivo na estrutura de diretórios correspondente ao nome do seu pacote (depois de extrair o arquivo .jar, ele deve estar no mesmo diretório que o arquivo .class) e acesse-o usando getClass().getResourceAsStream("file.txt")

  • Coloque o arquivo na raiz (depois de extrair o arquivo .jar, ele deve estar na raiz) e acesse-o usando Thread.currentThread().getContextClassLoader().getResourceAsStream("file.txt")

A primeira opção pode não funcionar quando o jar é usado como um plugin.

Se você quer ler como um arquivo, acredito que ainda há uma solução semelhante:

  ClassLoader classLoader = getClass().getClassLoader(); File file = new File(classLoader.getResource("file/test.xml").getFile()); 

Você também pode simplesmente usar o java.nio. Aqui está um exemplo para slurp no texto de um arquivo em resourcePath no classpath:

 new String(Files.readAllBytes(Paths.get(getClass().getResource(resourcePath).toURI()))) 

Certifique-se de trabalhar com o separador correto. Eu substituí todo / em um caminho relativo com um File.separator . Isso funcionou bem no IDE, no entanto, não funcionou no JAR de construção.

Eu tive esse problema antes e fiz o caminho de fallback para o carregamento. Basicamente, a primeira maneira de trabalhar com o arquivo .jar e a segunda maneira funciona dentro do eclipse ou outro IDE.

 public class MyClass { public static InputStream accessFile() { String resource = "my-file-located-in-resources.txt"; // this is the path within the jar file InputStream input = MyClass.class.getResourceAsStream("/resources/" + resource); if (input == null) { // this is how we load file within editor (eg eclipse) input = MyClass.class.getClassLoader().getResourceAsStream(resource); } return input; } } 

Até agora (dezembro de 2017), esta é a única solução que encontrei que funciona dentro e fora do IDE.

Use PathMatchingResourcePatternResolver

Nota: funciona também em spring-boot

Neste exemplo estou lendo alguns arquivos localizados em src / main / resources / my_folder :

 try { // Get all the files under this inner resource folder: my_folder String scannedPackage = "my_folder/*"; PathMatchingResourcePatternResolver scanner = new PathMatchingResourcePatternResolver(); Resource[] resources = scanner.getResources(scannedPackage); if (resources == null || resources.length == 0) log.warn("Warning: could not find any resources in this scanned package: " + scannedPackage); else { for (Resource resource : resources) { log.info(resource.getFilename()); // Read the file content (I used BufferedReader, but there are other solutions for that): BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(resource.getInputStream())); String line = null; while ((line = bufferedReader.readLine()) != null) { // ... // ... } bufferedReader.close(); } } } catch (Exception e) { throw new Exception("Failed to read the resources folder: " + e.getMessage(), e); } 

Depois de pesquisar bastante em Java, a única solução que parece funcionar para mim é ler manualmente o próprio arquivo jar, a menos que você esteja em um ambiente de desenvolvimento (IDE):

 /** @return The root folder or jar file that the class loader loaded from */ public static final File getClasspathFile() { return new File(YourMainClass.class.getProtectionDomain().getCodeSource().getLocation().getFile()); } /** @param resource The path to the resource * @return An InputStream containing the resource's contents, or * null if the resource does not exist */ public static final InputStream getResourceAsStream(String resource) { resource = resource.startsWith("/") ? resource : "/" + resource; if(getClasspathFile().isDirectory()) {//Development environment: return YourMainClass.class.getResourceAsStream(resource); } final String res = resource;//Jar or exe: return AccessController.doPrivileged(new PrivilegedAction() { @SuppressWarnings("resource") @Override public InputStream run() { try { final JarFile jar = new JarFile(getClasspathFile()); String resource = res.startsWith("/") ? res.substring(1) : res; if(resource.endsWith("/")) {//Directory; list direct contents:(Mimics normal getResourceAsStream("someFolder/") behaviour) ByteArrayOutputStream baos = new ByteArrayOutputStream(); Enumeration entries = jar.entries(); while(entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); if(entry.getName().startsWith(resource) && entry.getName().length() > resource.length()) { String name = entry.getName().substring(resource.length()); if(name.contains("/") ? (name.endsWith("/") && (name.indexOf("/") == name.lastIndexOf("/"))) : true) {//If it's a folder, we don't want the children's folders, only the parent folder's children! name = name.endsWith("/") ? name.substring(0, name.length() - 1) : name; baos.write(name.getBytes(StandardCharsets.UTF_8)); baos.write('\r'); baos.write('\n'); } } } jar.close(); return new ByteArrayInputStream(baos.toByteArray()); } JarEntry entry = jar.getJarEntry(resource); InputStream in = entry != null ? jar.getInputStream(entry) : null; if(in == null) { jar.close(); return in; } final InputStream stream = in;//Don't manage 'jar' with try-with-resources or close jar until the return new InputStream() {//returned stream is closed(closing the jar closes all associated InputStreams): @Override public int read() throws IOException { return stream.read(); } @Override public int read(byte b[]) throws IOException { return stream.read(b); } @Override public int read(byte b[], int off, int len) throws IOException { return stream.read(b, off, len); } @Override public long skip(long n) throws IOException { return stream.skip(n); } @Override public int available() throws IOException { return stream.available(); } @Override public void close() throws IOException { try { jar.close(); } catch(IOException ignored) { } stream.close(); } @Override public synchronized void mark(int readlimit) { stream.mark(readlimit); } @Override public synchronized void reset() throws IOException { stream.reset(); } @Override public boolean markSupported() { return stream.markSupported(); } }; } catch(Throwable e) { e.printStackTrace(); return null; } } }); } 

Nota: O código acima só parece funcionar corretamente para arquivos jar se estiver na class principal. Não sei porquê.

Se você estiver usando o spring, então você pode usar o seguinte método para ler o arquivo de src / main / resources:

 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import org.springframework.core.io.ClassPathResource; public String readFile() { StringBuilder result = new StringBuilder(""); ClassPathResource resource = new ClassPathResource("filename.txt"); try (InputStream inputStream = resource.getInputStream()) { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String line; while ((line = bufferedReader.readLine()) != null) { result.append(line); } inputStream.close(); } catch (IOException e) { e.printStackTrace(); } return result.toString(); }