Различные способы загрузки файла в качестве InputStream
Какая разница между:
InputStream is = this.getClass().getClassLoader().getResourceAsStream(fileName)
и
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)
и
InputStream is = this.getClass().getResourceAsStream(fileName)
Когда каждый из них более подходит для использования, чем другие?
Файл, который я хочу прочитать, находится в пути к классам как мой класс, который читает файл. Мой класс и файл находятся в одной банке и упакованы в файл EAR и развернуты в WebSphere 6.1.
Ответы
Ответ 1
Есть тонкие различия в том, как интерпретируется fileName
, который вы передаете. По сути, у вас есть 2 разных метода: ClassLoader.getResourceAsStream()
и Class.getResourceAsStream()
. Эти два метода найдут ресурс по-разному.
В Class.getResourceAsStream(path)
путь интерпретируется как локальный путь к пакету класса, из которого вы вызываете его. Например, вызов TG44 будет искать файл в вашем пути к классам в следующем месте: "java/lang/myfile.txt"
. Если ваш путь начинается с /
, то он будет считаться абсолютным путем и начнет поиск из корня пути к классам. Поэтому, вызывая String.getResourceAsStream("/myfile.txt")
, мы посмотрим на следующее место в вашем пути к классам ./myfile.txt
.
ClassLoader.getResourceAsStream(path)
будет считать все пути абсолютными. Поэтому вызовы String.getClassLoader().getResourceAsStream("myfile.txt")
и String.getClassLoader().getResourceAsStream("/myfile.txt")
будут искать файл в вашем пути к классам в следующем месте: ./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")
(ведущий /
удаляется).
Ответ 2
Используйте MyClass.class.getClassLoader().getResourceAsStream(path)
для загрузки ресурса, связанного с вашим кодом. Используйте MyClass.class.getResourceAsStream(path)
в качестве ярлыка и для ресурсов, упакованных в пакет вашего класса.
Используйте Thread.currentThread().getContextClassLoader().getResourceAsStream(path)
, чтобы получить ресурсы, которые являются частью кода клиента, а не жестко привязаны к вызывающему коду. Вы должны быть осторожны с этим, поскольку загрузчик класса контекста потока может указывать на что-либо.
Ответ 3
Обычная старая 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);
}
}
Ответ 4
Все эти ответы здесь, а также ответы в этом вопросе позволяют предположить, что загрузка абсолютных 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!
Извините, у меня нет абсолютно никакого объяснения, но я думаю, что tomcat делает грязные трюки и черную магию с загрузчиками классов и вызывают разницу. Я всегда использовал class.getResourceAsStream(String)
в прошлом и не имел никаких проблем.
PS: Я также разместил это над здесь
Ответ 5
Попробовав несколько способов загрузить файл безуспешно, я вспомнил, что могу использовать FileInputStream
, который отлично работал.
InputStream is = new FileInputStream("file.txt");
Это еще один способ чтения файла в InputStream
, он считывает файл из текущей запущенной папки.
Ответ 6
Это работает, попробуйте:
InputStream in_s1 = TopBrandData.class.getResourceAsStream("/assets/TopBrands.xml");