Diferentes maneiras de carregar um arquivo como um InputStream

Qual é a diferença entre:

InputStream is = this.getClass().getClassLoader().getResourceAsStream(fileName) 

e

 InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName) 

e

 InputStream is = this.getClass().getResourceAsStream(fileName) 

Quando cada um é mais apropriado para usar do que os outros?

O arquivo que eu quero ler está no classpath como minha class que lê o arquivo. Minha class e o arquivo estão no mesmo jar e empacotados em um arquivo EAR e implementados no WebSphere 6.1.

Existem diferenças sutis sobre como o nome do fileName você está passando é interpretado. Basicamente, você tem dois methods diferentes: ClassLoader.getResourceAsStream() e Class.getResourceAsStream() . Esses dois methods localizarão o recurso de maneira diferente.

Em Class.getResourceAsStream(path) , o caminho é interpretado como um caminho local para o pacote da class da qual você está chamando. Por exemplo chamando, String.getResourceAsStream("myfile.txt") irá procurar por um arquivo no seu classpath no seguinte local: "java/lang/myfile.txt" . Se o seu caminho começar com um / , ele será considerado um caminho absoluto e começará a pesquisar a partir da raiz do caminho de class. Portanto, chamar String.getResourceAsStream("/myfile.txt") examinará o seguinte local em seu caminho de class ./myfile.txt .

ClassLoader.getResourceAsStream(path) considerará todos os caminhos como absolutos. Portanto, chamar String.getClassLoader().getResourceAsStream("myfile.txt") e String.getClassLoader().getResourceAsStream("/myfile.txt") procurarão um arquivo em seu classpath no seguinte local: ./myfile.txt .

Toda vez que menciono um local neste post, pode ser um local em seu próprio sistema de arquivos ou dentro do arquivo jar correspondente, dependendo da class e / ou ClassLoader do qual você está carregando o recurso.

No seu caso, você está carregando a class de um Application Server, portanto, deve-se usar Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName) vez de this.getClass().getClassLoader().getResourceAsStream(fileName) . this.getClass().getResourceAsStream() também funcionará.

Leia este artigo para obter informações mais detalhadas sobre esse problema específico.


Aviso para usuários do Tomcat 7 e abaixo

Uma das respostas a essa pergunta afirma que minha explicação parece estar incorreta para o Tomcat 7. Tentei olhar em volta para ver por que esse seria o caso.

Então eu olhei para o código-fonte do WebAppClassLoader do Tomcat para várias versões do Tomcat. A implementação de findResource(String name) (que é a principal responsável por produzir a URL para o recurso solicitado) é virtualmente idêntica no Tomcat 6 e no Tomcat 7, mas é diferente no Tomcat 8.

Nas versões 6 e 7, a implementação não tenta normalizar o nome do recurso. Isso significa que, nessas versões, classLoader.getResourceAsStream("/resource.txt") pode não produzir o mesmo resultado que o classLoader.getResourceAsStream("resource.txt") embora deva (desde que o que o Javadoc especifica). [Código fonte]

Na versão 8, o nome do recurso é normalizado para garantir que a versão absoluta do nome do recurso seja a usada. Portanto, no Tomcat 8, as duas chamadas descritas acima devem sempre retornar o mesmo resultado. [Código fonte]

Como resultado, você precisa ter cuidado extra ao usar ClassLoader.getResourceAsStream() ou Class.getResourceAsStream() nas versões do Tomcat anteriores a 8. E também deve ter em mente que class.getResourceAsStream("/resource.txt") na verdade, chama classLoader.getResourceAsStream("resource.txt") (o principal / é removido).

Use MyClass.class.getClassLoader().getResourceAsStream(path) para carregar o recurso associado ao seu código. Use MyClass.class.getResourceAsStream(path) como um atalho e para resources incluídos no pacote da sua class.

Use Thread.currentThread().getContextClassLoader().getResourceAsStream(path) para obter resources que fazem parte do código do cliente, não vinculados ao código de chamada. Você deve ter cuidado com isso, pois o carregador de classs de contexto de thread pode estar apontando para qualquer coisa.

O velho Java simples no Java 7 antigo e nenhuma outra dependência demonstra a diferença …

Eu coloquei file.txt em c:\temp\ e coloquei c:\temp\ no classpath.

Há apenas um caso em que há uma diferença entre as duas chamadas.

 class J { public static void main(String[] a) { // as "absolute" // ok System.err.println(J.class.getResourceAsStream("/file.txt") != null); // pop System.err.println(J.class.getClassLoader().getResourceAsStream("/file.txt") != null); // as relative // ok System.err.println(J.class.getResourceAsStream("./file.txt") != null); // ok System.err.println(J.class.getClassLoader().getResourceAsStream("./file.txt") != null); // no path // ok System.err.println(J.class.getResourceAsStream("file.txt") != null); // ok System.err.println(J.class.getClassLoader().getResourceAsStream("file.txt") != null); } } 

Todas essas respostas por aqui, bem como as respostas nesta questão , sugerem que carregar URLs absolutas, como “/foo/bar.properties”, é tratado da mesma forma por class.getResourceAsStream(String) e class.getClassLoader().getResourceAsStream(String) . Este não é o caso, pelo menos não na minha configuração / versão do Tomcat (atualmente 7.0.40).

 MyClass.class.getResourceAsStream("/foo/bar.properties"); // works! MyClass.class.getClassLoader().getResourceAsStream("/foo/bar.properties"); // does NOT work! 

Desculpe, eu não tenho absolutamente nenhuma explicação satisfatória, mas eu acho que o gato faz truques sujos e sua magia negra com os classloaders e causam a diferença. Eu sempre usei class.getResourceAsStream(String) no passado e não tive nenhum problema.

PS: Eu também postei isso aqui

Depois de tentar algumas maneiras de carregar o arquivo sem sucesso, lembrei que podia usar o FileInputStream , que funcionava perfeitamente.

 InputStream is = new FileInputStream("file.txt"); 

Esta é outra maneira de ler um arquivo em um InputStream , ele lê o arquivo da pasta atualmente em execução.

Funciona, experimente isto:

 InputStream in_s1 = TopBrandData.class.getResourceAsStream("/assets/TopBrands.xml");