getResourceAsStream retorna nulo

183

Estou carregando um arquivo de texto de dentro de um pacote em um JAR compilado do meu projeto Java. A estrutura de diretórios relevante é a seguinte:

/src/initialization/Lifepaths.txt

Meu código carrega um arquivo chamando Class::getResourceAsStreampara retornar a InputStream.

public class Lifepaths {
    public static void execute() {
        System.out.println(Lifepaths.class.getClass().
            getResourceAsStream("/initialization/Lifepaths.txt"));
    }

    private Lifepaths() {}

    //This is temporary; will eventually be called from outside
    public static void main(String[] args) {execute();}
}

A impressão sempre será impressa null, não importa o que eu use. Não sei por que o que foi dito acima não funcionaria, então também tentei:

  • "/src/initialization/Lifepaths.txt"
  • "initialization/Lifepaths.txt"
  • "Lifepaths.txt"

Nenhum desses trabalhos. Eu li várias perguntas até agora sobre o tópico, mas nenhuma delas foi útil - geralmente, elas dizem apenas para carregar arquivos usando o caminho raiz, o que eu já estou fazendo. Isso, ou apenas carregue o arquivo do diretório atual (apenas carregue filename), o que eu também tentei. O arquivo está sendo compilado no JAR no local apropriado com o nome apropriado.

Como eu resolvo isso?

Basil Bourque
fonte
3
Você verificou se realmente está no arquivo jar? Você verificou a caixa do arquivo?
22813 Jon Skeet
@ JonSkeet De fato, está sendo compilado no arquivo JAR no local apropriado e o caso está correto.
1
@greedybuddha Embora não possa invocar isso de um contexto estático, posso invocá-lo usando Lifepaths.class. Dito isto, por que getClassLoader()permite que funcione? (Além disso, sinta-se livre para postar uma resposta!)
Você pode mostrar Lifepaths.getClass()? Não existe esse método estático definido em Object ... #
Puce
1
Veja esta resposta e veja se consegue fazê-la funcionar getResource(String). BTW - Eu sempre tive problemas para fazer com que qualquer um trabalhasse em um staticcontexto. O problema é basicamente que o carregador de classes obtido é o destinado às classes J2SE. Você precisa obter acesso ao carregador de classes de contexto destinado ao próprio aplicativo.
Andrew Thompson

Respostas:

159

Lifepaths.class.getClass().getResourceAsStream(...) carrega recursos usando o carregador de classes do sistema, ele obviamente falha porque não vê seus JARs

Lifepaths.class.getResourceAsStream(...) carrega recursos usando o mesmo carregador de classe que carregou a classe Lifepaths e deve ter acesso aos recursos em seus JARs

hoaz
fonte
129
Apenas para adicionar, ao chamar getResourceAsStream (name), o nome deve começar com "/". Não tenho certeza se isso é necessário, mas tenho um problema sem ele.
David
3
Eu estou ferrando com isso desde as 8h desta manhã de ontem. Me salvou. Eu também precisava de uma barra líder para fazê-lo funcionar.
Kyle
4
Lembre-se também de que a fonte desejada pode estar fora da hierarquia de pacotes. Nesse caso, você terá que usar "../" no seu caminho para subir um nível e depois descer para outro ramo do caminho para alcançar seu recurso.
Zon
3
@ David - Eu acho que (líder '/') é necessário caso contrário, ele procura em relação ao pacote Lifepaths.class
Mz A
5
Apenas para adicionar algumas informações, você precisa adicionar um /antes do seu caminho, se o arquivo estiver em um diretório diferente; por exemplo initialization/Lifepaths.txt. Se o caminho do arquivo for o mesmo da sua classe (mas com os recursos do diretório principal), basta colocar o nome do arquivo sem nenhum /. Por exemplo, se sua classe tiver o seguinte caminho src/main/java/paths/Lifepaths.java, seu arquivo deverá ter esse caminho src/main/resources/paths/Lifepaths.txt.
Dwhitz 6/07
60

As regras são as seguintes:

  1. verifique o local do arquivo que você deseja carregar dentro do JAR (e, assim, verifique também se ele foi realmente adicionado ao JAR)
  2. use um caminho absoluto: o caminho começa na raiz do JAR
  3. use um caminho relativo: o caminho começa no diretório de pacotes da classe que você está chamando getResource / getResoucreAsStream

E tentar:

Lifepaths.class.getResourceAsStream("/initialization/Lifepaths.txt")

ao invés de

Lifepaths.class.getClass().getResourceAsStream("/initialization/Lifepaths.txt")

(não tenho certeza se isso faz diferença, mas o primeiro usará o ClassLoader / JAR correto, enquanto eu não tiver certeza do último)

Puce
fonte
2
Eu já fiz todas essas três coisas. Por favor, releia minha pergunta.
Da sua pergunta não está claro o que "A estrutura de diretório relevante" é e se você realmente verificado se e onde o arquivo está localizado no JAR (passo 1)
Puce
1
Sua observação sobre o caminho relativo finalmente resolveu o problema no meu final; obrigado!
Paul Bormans
Interessante. Acontece que eu tive que fazer o "/config.properties" (com uma barra) para chegar até ele ...
Erk 01/06
45

Portanto, existem várias maneiras de obter um recurso de um jar e cada uma possui uma sintaxe ligeiramente diferente, onde o caminho precisa ser especificado de maneira diferente.

A melhor explicação que eu vi é este artigo do JavaWorld . Resumirei aqui, mas se você quiser saber mais, consulte o artigo.

Métodos

1) ClassLoader.getResourceAsStream().

Formato: "/" - nomes separados; sem "/" inicial (todos os nomes são absolutos).

Exemplo: this.getClass().getClassLoader().getResourceAsStream("some/pkg/resource.properties");

2) Class.getResourceAsStream()

Formato: "/" - nomes separados; "/" inicial indica nomes absolutos; todos os outros nomes são relativos ao pacote da classe

Exemplo: this.getClass().getResourceAsStream("/some/pkg/resource.properties");

greedybuddha
fonte
seu segundo exemplo está quebrado
Janus Troelsen 15/02
1
O segundo exemplo não está quebrado, como eu disse na resposta, depende de onde o recurso está localizado.
greedybuddha
Além disso: verifique se o seu IDE vê o arquivo ('some / pkg / resource.properties') atualizando a pasta de origem.
Eric Duminil
15

Não use caminhos absolutos, torne-os relativos ao diretório 'recursos' no seu projeto. Código rápido e sujo que exibe o conteúdo do MyTest.txt do diretório 'recursos'.

@Test
public void testDefaultResource() {
    // can we see default resources
    BufferedInputStream result = (BufferedInputStream) 
         Config.class.getClassLoader().getResourceAsStream("MyTest.txt");
    byte [] b = new byte[256];
    int val = 0;
    String txt = null;
    do {
        try {
            val = result.read(b);
            if (val > 0) {
                txt += new String(b, 0, val);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } 
    } while (val > -1);
    System.out.println(txt);
}
Paul Smith
fonte
9

Você pode tentar isso para obter o fluxo, ou seja, primeiro obtenha o URL e abra-o como fluxo.

URL url = getClass().getResource("/initialization/Lifepaths.txt"); 
InputStream strm = url.openStream(); 

Uma vez tive uma pergunta semelhante: a leitura do arquivo txt do jar falha, mas a leitura da imagem funciona

MrD
fonte
3
este é exatamente o que getResourceAsStream () faz
fedeb
8

Parece haver um problema com o ClassLoader que você está usando. Use o contextClassLoader para carregar a classe. Independentemente de estar em um método estático / não estático

Thread.currentThread().getContextClassLoader().getResourceAsStream......

Binita Bharati
fonte
6

Eu me encontrei em uma questão semelhante. Como estou usando o maven, precisava atualizar meu pom.xml para incluir algo como isto:

   ...
</dependencies>
<build>
    <resources>
        <resource>
            <directory>/src/main/resources</directory>
        </resource>
        <resource>
            <directory>../src/main/resources</directory>
        </resource>
    </resources>
    <pluginManagement>
        ...

Observe a etiqueta de recurso para especificar onde está essa pasta. Se você tiver projetos aninhados (como eu), convém obter recursos de outras áreas em vez de apenas no módulo em que está trabalhando. Isso ajuda a reduzir a manutenção do mesmo arquivo em cada repositório, se você estiver usando dados de configuração semelhantes

plosco
fonte
3

O que funcionou para mim foi adicionar o arquivo My Project/Java Resources/srce usar

this.getClass().getClassLoader().getResourceAsStream("myfile.txt");

Não precisei adicionar explicitamente esse arquivo ao caminho (adicioná-lo a /srcisso aparentemente)

CommonCoreTawan
fonte
Sintaxe java errada
Ilya Kharlamov 29/11
2

Não sei se da ajuda, mas no meu caso, eu tinha meu recurso na pasta / src / e estava recebendo esse erro. Em seguida, mudei a imagem para a pasta bin e ela corrigiu o problema.

Leo Ufimtsev
fonte
2

Verifique se o diretório de recursos (por exemplo, "src") está no seu caminho de classe (verifique se é um diretório de origem no caminho de construção no eclipse).

Verifique se o clazz está carregado no carregador de classe principal.

Em seguida, para carregar src / initialization / Lifepaths.txt, use

clazz.getResourceAsStream("/initialization/Lifepaths.txt");

Por que: clazz.getResourcesAsStream(foo)procura dentro do caminho de classe do clazz , em relação ao diretório em que o clazz vive . O "/" principal faz com que seja carregado a partir da raiz de qualquer diretório no caminho de classe do clazz.

A menos que você esteja em um contêiner de algum tipo, como o Tomcat, ou esteja fazendo algo diretamente com os ClassLoaders, é possível tratar apenas o caminho de classe do eclipse / linha de comando como o único caminho de classe do carregador de classes.

Bobby Martin
fonte
2

O carregador de classe JVM padrão usará pai-carregador de classe para carregar recursos primeiro: deletegate-parent-classloader.

Lifepaths.class.getClass()O classloader de bootstrap classloader, portanto getResourceAsStream, pesquisará apenas $ JAVA_HOME, independentemente do usuário fornecido classpath. Obviamente, Lifepaths.txt não está lá.

Lifepaths.classO classloader de é system classpath classloader, portanto, getResourceAsStreamserá pesquisado definido pelo usuário classpathe Lifepaths.txt estará lá.

Ao usar java.lang.Class#getResourceAsStream(String name), o nome que não inicia com '/' será adicionado package namecomo prefixo. Se você quiser evitar isso, use java.lang.ClassLoader#getResourceAsStream. Por exemplo:

ClassLoader loader = Thread.currentThread().getContextClassLoader();
String resourceName = "Lifepaths.txt";
InputStream resourceStream = loader.getResourceAsStream(resourceName); 
ridox
fonte
2

A grosso modo:

getClass().getResource("/") ~ = Thread.currentThread().getContextClassLoader().getResource(".")

Suponha que a estrutura do seu projeto seja a seguinte:

├── src
   ├── main
   └── test
   └── test
       ├── java
          └── com
              └── github
                  └── xyz
                      └── proj
                          ├── MainTest.java
                          └── TestBase.java
       └── resources
           └── abcd.txt
└── target
    └── test-classes
        ├── com
        └── abcd.txt
// in MainClass.java
this.getClass.getResource("/") -> "~/proj_dir/target/test-classes/"
this.getClass.getResource(".") -> "~/proj_dir/target/test-classes/com/github/xyz/proj/"
Thread.currentThread().getContextClassLoader().getResources(".") -> "~/proj_dir/target/test-classes/"
Thread.currentThread().getContextClassLoader().getResources("/") ->  null
Shijing Lv
fonte
2

se você estiver usando o Maven, verifique se a sua embalagem é 'jar' e não 'pom'.

<packaging>jar</packaging>
Feku279
fonte
0

O que você realmente precisa é de um classPath absoluto completo para o arquivo. Portanto, em vez de adivinhar, tente descobrir o ROOT e mova o arquivo para um local melhor, com base nas estruturas de arquivos <.war> ...

URL test1 = getClass().getResource("/");
URL test2 = getClass().getClassLoader().getResource("/");            
URL test3 = getClass().getClassLoader().getResource("../");

logger.info(test1.getPath()); 
logger.info(test2.getPath());
logger.info(test3.getPath());
ConAim
fonte
0

O que funcionou para mim é que coloquei o arquivo em

src/main/java/myfile.log

e

InputStream is = getClass().getClassLoader().getResourceAsStream("myfile.log");
        
        if (is == null) {
            throw new FileNotFoundException("Log file not provided");
        }
Akash Yellappa
fonte
A pasta de origem é chamada src/main/JAVA, obviamente seus arquivos que não são de código não devem estar localizados aqui.
leyren 6/07
-12

@Emracool ... eu sugiro uma alternativa. Desde que você parece estar tentando carregar um arquivo * .txt. Melhor usar FileInputStream()do que isso getClass().getClassLoader().getResourceAsStream()ou irritante getClass().getResourceAsStream(). Pelo menos seu código será executado corretamente.

Jain
fonte
que ??? -1 para essa resposta de trabalho. Não importa o que. Mas a solução sugerida acima funcionará com certeza.
Jain