getResourceAsStream () против FileInputStream

173

Я пытался загрузить файл в веб-приложение, и я получал FileNotFoundисключение при использовании FileInputStream. Однако, используя тот же путь, я смог загрузить файл, когда я это сделал getResourceAsStream(). В чем разница между этими двумя методами, и почему один работает, а другой нет?

Вивин Палиат
источник

Ответы:

256

И java.io.Fileдействует на локальной файловой системе диска. Основная причина вашей проблемы в том, что относительные пути в java.ioзависимости от текущего рабочего каталога. Т.е. каталог, из которого запускается JVM (в вашем случае это сервер). Например, это может быть C:\Tomcat\binили что-то совершенно другое, но, таким образом, нет, C:\Tomcat\webapps\contextname или то, что вы ожидаете. В обычном проекте Eclipse это было бы C:\Eclipse\workspace\projectname. Вы можете узнать о текущем рабочем каталоге следующим образом:

System.out.println(new File(".").getAbsolutePath());

Однако рабочий каталог никоим образом не является программно управляемым. Вы действительно должны предпочитать использовать абсолютные пути в FileAPI вместо относительных путей. Например C:\full\path\to\file.ext.

Вы не хотите жестко кодировать или угадывать абсолютный путь в Java (веб) приложениях. Это только проблема переносимости (то есть она работает в системе X, но не в системе Y). Обычной практикой является размещение таких ресурсов в пути к классам или добавление их полного пути к пути к классам (в IDE, такой как Eclipse, это srcпапка и «путь сборки» соответственно). Таким образом , вы можете получить их с помощью объекта с ClassLoaderпомощью ClassLoader#getResource()или ClassLoader#getResourceAsStream(). Он может найти файлы относительно «корня» пути к классам, как вы случайно поняли. В веб-приложениях (или любых других приложениях, использующих несколько загрузчиков классов) рекомендуется использовать для этого ClassLoaderвозвращаемое значение, Thread.currentThread().getContextClassLoader()чтобы вы также могли смотреть «вне» контекста веб-приложения.

Другой альтернативой в веб-приложениях является ServletContext#getResource()и его аналог ServletContext#getResourceAsStream(). Он может получить доступ к файлам, расположенным в webобщей папке проекта webapp, включая эту /WEB-INFпапку. ServletContextДоступен в сервлетах унаследованного getServletContext()метода, вы можете назвать это как есть.

Смотрите также:

BalusC
источник
5
@khylo: связанный: stackoverflow.com/questions/7952090/…
BalusC
27

getResourceAsStream это правильный способ сделать это для веб-приложений (как вы уже узнали).

Причина в том, что чтение из файловой системы не может работать, если вы упакуете свое веб-приложение в WAR. Это правильный способ упаковки веб-приложения. Это переносимо, потому что вы не зависите от абсолютного пути к файлу или местоположения, где установлен ваш сервер приложений.

duffymo
источник
3
+1 - хотя "не может работать" слишком сильно. (Чтение из файловой системы может быть сделано для работы, но делать это переносимо довольно сложно ... и намного больше кода, особенно если ресурс находится в JAR.)
Стивен К.
1
Даффи, очень хороший ответ, и вы объяснили, в чем заключалась моя ошибка, но BalusC подробно рассказал - я думаю, что его ответ был бы полезен для людей, которые хотели бы знать и внутренние детали. Надеюсь, ты не возражаешь, чтобы я сменил принятый ответ на его!
Вивин Палиат
@ Стефан - я не думаю, что "не может работать" слишком сильно. Даже такая простая вещь, как развертывание на двух разных серверах с разными путями к серверу приложений, сломает его. Дело в том, что вам нужно сделать свою WAR максимально автономной. Ваша точка зрения верна, но я собираюсь придерживаться своей формулировки.
duffymo
14

FileInputStream загрузит путь к файлу, который вы передаете конструктору относительно рабочего каталога Java-процесса. Обычно в веб-контейнере это что-то вроде binпапки.

getResourceAsStream()загрузит путь к файлу относительно пути к классу вашего приложения .

Мэтт Б
источник
12

FileInputStreamКласс работает непосредственно с основной файловой системой. Если рассматриваемый файл не присутствует там физически, он не сможет открыть его. getResourceAsStream()Метод работает по- другому. Он пытается найти и загрузить ресурс, используя ClassLoaderкласс, к которому он обращен. Это позволяет ему, например, находить ресурсы, встроенные в jarфайлы.

кортик
источник
Ну, файлы внутри jar все еще физически «присутствуют» в файловой системе, просто содержатся в других файлах
matt b
1
Ну да, конечно. Но они обычно не рассматриваются как независимые объекты в файловой системе, если только ваше приложение не знает о jarформате файла и его последствиях. И в Java соответствующие ClassLoaderмогут обладать этими знаниями, тогда как в равной степени, FileInputStreamконечно, нет.
Дирк
7

classname.getResourceAsStream () загружает файл через загрузчик классов с именем класса. Если класс пришел из jar-файла, именно из него будет загружен ресурс.

FileInputStream используется для чтения файла из файловой системы.

Лахлан Рош
источник
0

Я здесь, разделяя оба использования, помечая их как «Чтение файла» (java.io) и «Чтение ресурса» (ClassLoader.getResourceAsStream ()).

Чтение файлов - 1. Работает в локальной файловой системе. 2. Пытается найти файл, запрошенный из текущего каталога, запущенного JVM, от имени пользователя root 3. Идеально подходит для использования файлов для обработки в заранее определенном месте, например, / dev / files или C: \ Data.

Resource Read - 1. Работает с путем к классу 2. Пытается найти файл / ресурс в текущем или родительском пути к загрузчику классов. 3. Идеально подходит для загрузки файлов из упакованных файлов, таких как war или jar.

Адитья Бхуян
источник