getResourceAsStream () проти FileInputStream


173

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

Відповіді:


256

Команда " java.io.Fileand consorts" діє в файловій системі локального диска. Першопричиною вашої проблеми є те, що відносні шляхи в 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). Звичайна практика полягає в тому, щоб розмістити такі види ресурсів на classpath або додати його повний шлях до classpath (у IDE, як Eclipse, який є відповідною srcпапкою та "path path"). Таким чином , ви можете отримати їх за допомогою об'єкта з ClassLoaderдопомогою ClassLoader#getResource()або ClassLoader#getResourceAsStream(). Він здатний знаходити файли відносно "кореня" classpath, як ви за збігом обставин з'ясували. У Webapplications (або будь-який інший додаток, який використовує кілька завантажувачів класів) рекомендується використовувати ClassLoaderяк повернуті Thread.currentThread().getContextClassLoader()для цього, щоб ви могли виглядати "поза" контексту webapp.

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

Дивитися також:


5
@khylo: пов'язані з : stackoverflow.com/questions/7952090 / ...
BalusC

27

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

Причина полягає в тому, що читання з файлової системи не може працювати, якщо ви пакуєте веб-додаток у рамках війни. Це правильний спосіб упаковки веб-програми. Це портативно, тому що ви не залежать від абсолютного шляху до файлу чи місця, де встановлений ваш сервер додатків.


3
+1 - хоча "не може працювати" занадто сильно. (Читання з файлової системи може бути змушене працювати, але робити це портативно - хитро ... і набагато більше коду, особливо якщо ресурс знаходиться в JAR.)
Stephen C,

1
сумна, дуже приємна відповідь, і ви пояснили, у чому моя помилка, але BalusC детально розібрався - я думаю, що його відповідь буде корисною для людей, які хотіли б також знати і внутрішні деталі. Сподіваюся, ви не заперечуєте, щоб я змінив прийняту відповідь на його!
Вівін Паліат

@Stephen - Я не думаю, що "не може працювати" занадто сильно. Навіть щось таке просте, як розгортання на двох різних серверах з різними шляхами до сервера додатків, порушить це. Справа в тому, що вам потрібно зробити свою ВІЙНА максимально автономною. Ваша думка правильна, але я буду дотримуватися своєї формулювання.
duffymo

14

FileInputStream завантажить шлях файлу, який ви передаєте конструктору, відносно робочого каталогу Java-процесу. Зазвичай у веб-контейнері це щось на зразок binпапки.

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


12

FileInputStreamКлас працює безпосередньо з основною файловою системою. Якщо відповідний файл там фізично не присутній, його не вдасться відкрити. getResourceAsStream()Метод працює по- іншому. Він намагається знайти та завантажити ресурс за допомогою ClassLoaderкласу, на який він викликається. Це дозволяє йому знаходити, наприклад, ресурси, вбудовані у jarфайли.


Ну, а файли в банці все ще фізично "присутні" у файловій системі, що міститься в інших файлах
matt b

1
Ну так, звичайно. Але вони, як правило, не розглядаються як незалежні об'єкти у файловій системі, якщо тільки у вашій програмі не трапляється знати про jarформат файлу та його наслідки. І на Яві відповідні ClassLoaderможуть мати ці знання, тоді як звичайна FileInputStreamправда не має.
Дірк

7

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

FileInputStream використовується для зчитування файлу з файлової системи.


0

Я тут, розділяючи обидва звички, позначаючи їх як Файл Прочитання (java.io) та Ресурс Читання (ClassLoader.getResourceAsStream ()).

Читання файлів - 1. Працює в локальній файловій системі. 2. Намагається знайти файл, який запитується з поточного каталогу, запущеного JVM, як корінь 3. Ідеально добре при використанні файлів для обробки у заздалегідь визначеному місці, наприклад, / dev / files або C: \ Data.

Читання ресурсів - 1. Працює на шляху до класу 2. Намагається знайти файл / ресурс у поточному або батьківському класі завантажувача. 3. Ідеально добре при спробі завантажувати файли з упакованих файлів, таких як війна чи jar.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.