getResourceAsStream () vs FileInputStream


173

Próbowałem załadować plik do aplikacji internetowej i otrzymałem FileNotFoundwyjątek, gdy użyłem FileInputStream. Jednak korzystając z tej samej ścieżki, udało mi się załadować plik, gdy to zrobiłem getResourceAsStream(). Jaka jest różnica między tymi dwiema metodami i dlaczego jedna działa, a druga nie?

Odpowiedzi:


256

java.io.FileI małżonki działa na lokalnym systemie plików na dysku. Główną przyczyną problemu jest to, że ścieżki względne w programie java.iosą zależne od bieżącego katalogu roboczego. To znaczy katalog, z którego uruchamiana jest maszyna JVM (w twoim przypadku: serwer WWW). Może to być na przykład C:\Tomcat\bincoś zupełnie innego, ale tym samym nie C:\Tomcat\webapps\contextname lub cokolwiek byś tego oczekiwał. W normalnym projekcie Eclipse to byłoby C:\Eclipse\workspace\projectname. Możesz dowiedzieć się o aktualnym katalogu roboczym w następujący sposób:

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

Jednak katalog roboczy nie jest w żaden sposób sterowany programowo. Naprawdę powinieneś preferować używanie ścieżek bezwzględnych w FileAPI zamiast ścieżek względnych. Np C:\full\path\to\file.ext.

Nie chcesz na stałe kodować ani zgadywać bezwzględnej ścieżki w aplikacjach Java (internetowych). To tylko problem z przenośnością (tj. Działa w systemie X, ale nie w systemie Y). Normalną praktyką jest umieszczanie tego rodzaju zasobów w ścieżce klas lub dodawanie ich pełnej ścieżki do ścieżki klas (w środowisku IDE takim jak Eclipse jest to odpowiednio srcfolder i „ścieżka budowania”). W ten sposób możesz je złapać za pomocą ClassLoaderby ClassLoader#getResource()lub ClassLoader#getResourceAsStream(). Jest w stanie zlokalizować pliki względem „katalogu głównego” ścieżki klas, jak przez przypadek się zorientowałeś. W aplikacjach internetowych (lub innych aplikacjach, które używają wielu programów ładujących), zaleca się użycie w tym celu ClassLoaderzwróconej przez, Thread.currentThread().getContextClassLoader()aby można było również szukać „poza” kontekstem aplikacji internetowej.

Inną alternatywą w aplikacjach internetowych jest ServletContext#getResource()i jego odpowiednik ServletContext#getResourceAsStream(). Jest w stanie uzyskać dostęp do plików znajdujących się w webfolderze publicznym projektu aplikacji internetowej, w tym do /WEB-INFfolderu. Jest ServletContexton dostępny w serwletach metodą dziedziczoną getServletContext(), możesz go nazwać tak, jak jest.

Zobacz też:



27

getResourceAsStream to właściwy sposób na zrobienie tego w przypadku aplikacji internetowych (jak już wiesz).

Powodem jest to, że odczyt z systemu plików nie może działać, jeśli spakujesz swoją aplikację internetową w WAR. To jest właściwy sposób pakowania aplikacji internetowej. W ten sposób jest przenośny, ponieważ nie jesteś zależny od bezwzględnej ścieżki pliku ani lokalizacji, w której jest zainstalowany serwer aplikacji.


3
+1 - chociaż „nie działa” jest zbyt mocne. (Czytanie z systemu plików może działać, ale robienie tego przenośnie jest trudne ... i dużo więcej kodu, zwłaszcza jeśli zasób jest w JAR.)
— Stephen C

1
duffy, bardzo miła odpowiedź i wyjaśniłeś, jaki był mój błąd, ale BalusC przeszedł do wielu szczegółów - myślę, że jego odpowiedź byłaby pomocna dla osób, które chciałyby również poznać szczegóły wewnętrzne. Mam nadzieję, że nie masz nic przeciwko temu, żebym zmienił zaakceptowaną odpowiedź na jego!
— Vivin Paliath

@Stephen - nie sądzę, że „nie mogę pracować” jest zbyt mocne. Nawet coś tak prostego, jak wdrożenie na dwóch różnych serwerach z różnymi ścieżkami do serwera aplikacji, zepsuje to. Chodzi o to, że musisz sprawić, by twoja WOJNA była jak najbardziej niezależna. Twój punkt widzenia jest słuszny, ale zamierzam trzymać się swojego sformułowania.
— duffymo

14

FileInputStream załaduje ścieżkę pliku przekazaną do konstruktora jako względną z katalogu roboczego procesu Java. Zwykle w kontenerze WWW jest to coś w rodzaju binfolderu.

getResourceAsStream()załaduje ścieżkę do pliku względną ze ścieżki klas aplikacji .


12

FileInputStreamKlasa współpracuje bezpośrednio z podstawowym systemem plików. Jeśli danego pliku nie ma tam fizycznie, nie uda się go otworzyć. getResourceAsStream()Metoda działa inaczej. Próbuje zlokalizować i załadować zasób przy użyciu ClassLoaderklasy, do której jest wywoływany. Umożliwia to na przykład wyszukiwanie zasobów osadzonych w jarplikach.


Cóż, pliki w słoiku nadal są fizycznie „obecne” w systemie plików, tylko zawarte w innych plikach
— mat b

1
Cóż, tak, oczywiście. Ale zazwyczaj nie są postrzegane jako niezależne jednostki w systemie plików, chyba że aplikacja wie o jarformacie pliku i jego konsekwencjach. A w Javie odpowiedni ClassLoadermoże mieć tę wiedzę, podczas gdy zwykły z FileInputStreampewnością nie.
— Dirk

7

classname.getResourceAsStream () ładuje plik za pośrednictwem modułu ładującego klasy nazwa_klasy. Jeśli klasa pochodzi z pliku jar, jest to miejsce, z którego zostanie załadowany zasób.

FileInputStream służy do odczytu pliku z systemu plików.


0

Jestem tutaj, oddzielając oba zastosowania, oznaczając je jako odczyt pliku (java.io) i odczyt zasobów (ClassLoader.getResourceAsStream ()).

Odczyt pliku - 1. Działa w lokalnym systemie plików. 2. Próbuje zlokalizować plik żądany z bieżącego katalogu uruchomionego JVM jako root 3. Idealnie sprawdza się w przypadku używania plików do przetwarzania w określonej lokalizacji, takiej jak / dev / files lub C: \ Data.

Odczyt zasobu - 1. Działa na ścieżce klasy. 2. Próbuje zlokalizować plik / zasób w bieżącej lub nadrzędnej ścieżce klasy modułu ładującego klasy. 3. Idealnie sprawdza się przy ładowaniu plików z plików spakowanych, takich jak war lub jar.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.