Различные способы загрузки файла в качестве InputStream - PullRequest
203 голосов
/ 24 марта 2009

Какая разница между:

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

и

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

и

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

Когда каждый из них более подходит для использования, чем другие?

Файл, который я хочу прочитать, находится в classpath как мой класс, который читает файл. Мой класс и файл находятся в одном банке, упакованы в файл EAR и развернуты в WebSphere 6.1.

Ответы [ 6 ]

278 голосов
/ 24 марта 2009

Существуют тонкие различия в том, как интерпретируется fileName, который вы передаете. В основном, у вас есть 2 разных метода: ClassLoader.getResourceAsStream() и Class.getResourceAsStream(). Эти два метода найдут ресурс по-разному.

В Class.getResourceAsStream(path) путь интерпретируется как локальный путь к пакету класса, из которого вы вызываете его. Например, String.getResourceAsStream("myfile.txt") будет искать файл в вашем пути к классам в следующем месте: "java/lang/myfile.txt". Если ваш путь начинается с /, то он будет считаться абсолютным путем и начнет поиск из корня пути к классам. Таким образом, вызов String.getResourceAsStream("/myfile.txt") приведет к поиску следующего местоположения в вашем пути к классам ./myfile.txt.

ClassLoader.getResourceAsStream(path) будет считать все пути абсолютными. Поэтому вызовы String.getClassLoader().getResourceAsStream("myfile.txt") и String.getClassLoader().getResourceAsStream("/myfile.txt") будут искать файл в вашем classpath по следующему адресу: ./myfile.txt.

Каждый раз, когда я упоминаю местоположение в этом посте, это может быть местоположение в самой вашей файловой системе или внутри соответствующего файла JAR, в зависимости от класса и / или ClassLoader, из которого вы загружаете ресурс.

В вашем случае вы загружаете класс с сервера приложений, поэтому вы должны использовать Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName) вместо this.getClass().getClassLoader().getResourceAsStream(fileName). this.getClass().getResourceAsStream() тоже будет работать.

Прочтите эту статью для получения более подробной информации об этой конкретной проблеме.


Предупреждение для пользователей Tomcat 7 и ниже

Один из ответов на этот вопрос гласит, что мое объяснение кажется неправильным для Tomcat 7. Я попытался осмотреться, чтобы понять, почему это так.

Итак, я посмотрел исходный код Tomcat WebAppClassLoader для нескольких версий Tomcat. Реализация findResource(String name) (которая в конечном итоге отвечает за создание URL-адреса запрашиваемого ресурса) практически идентична в Tomcat 6 и Tomcat 7, но отличается в Tomcat 8.

В версиях 6 и 7 реализация не пытается нормализовать имя ресурса. Это означает, что в этих версиях classLoader.getResourceAsStream("/resource.txt") может не давать тот же результат, что и событие classLoader.getResourceAsStream("resource.txt"), хотя это должно происходить (начиная с того, что указано в Javadoc). [исходный код]

В версии 8 имя ресурса нормализовано, чтобы гарантировать, что используется абсолютная версия имени ресурса. Поэтому в Tomcat 8 два вызова, описанные выше, всегда должны возвращать один и тот же результат. [исходный код]

В результате вы должны быть особенно осторожны при использовании ClassLoader.getResourceAsStream() или Class.getResourceAsStream() в версиях Tomcat более ранних, чем 8. И вы также должны помнить, что class.getResourceAsStream("/resource.txt") на самом деле вызывает classLoader.getResourceAsStream("resource.txt") (ведущий * 1054) * раздет).

20 голосов
/ 24 марта 2009

Используйте MyClass.class.getClassLoader().getResourceAsStream(path) для загрузки ресурса, связанного с вашим кодом. Используйте MyClass.class.getResourceAsStream(path) в качестве ярлыка и для ресурсов, упакованных в пакет вашего класса.

Используйте Thread.currentThread().getContextClassLoader().getResourceAsStream(path), чтобы получить ресурсы, которые являются частью клиентского кода, а не тесно связаны с вызывающим кодом. Вы должны быть осторожны с этим, поскольку загрузчик класса контекста потока может указывать на что угодно.

5 голосов
/ 09 июля 2015

Обычная старая Java на простой старой Java 7 и никакие другие зависимости не демонстрируют разницу ...

Я положил file.txt в c:\temp\ и положил c:\temp\ на путь к классам.

Существует только один случай, когда между двумя вызовами есть разница.

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); 
  }
}
3 голосов
/ 19 ноября 2013

Все эти ответы здесь, а также ответы в на этот вопрос предполагают, что загрузка абсолютных URL, таких как "/foo/bar.properties", обрабатывается одинаково при class.getResourceAsStream(String) и class.getClassLoader().getResourceAsStream(String) , Это НЕ так, по крайней мере, в моей конфигурации / версии Tomcat (в настоящее время 7.0.40).

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

Извините, у меня нет абсолютно никакого удовлетворительного объяснения, но я думаю, что кот делает грязные трюки и свою черную магию с загрузчиками классов и вызывает разницу. Я всегда использовал class.getResourceAsStream(String) в прошлом и у меня не было проблем.

PS: Я также разместил это более здесь

0 голосов
/ 20 марта 2018

Попробовав несколько способов загрузить файл безуспешно, я вспомнил, что могу использовать FileInputStream, который отлично работал.

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

Это еще один способ прочитать файл в InputStream, он читает файл из текущей запущенной папки.

0 голосов
/ 02 июля 2014

Это работает, попробуйте это:

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