Istnieją subtelne różnice w fileName
interpretacji tego, co mijasz. Zasadniczo masz 2 różne metody: ClassLoader.getResourceAsStream()
i Class.getResourceAsStream()
. Te dwie metody będą lokalizować zasób inaczej.
W Class.getResourceAsStream(path)
, ścieżka jest interpretowana jako lokalna ścieżka do pakietu klasy, z której ją wywołujesz. Na przykład powołania, String.getResourceAsStream("myfile.txt")
będzie szukał pliku w ścieżce klas w następującej lokalizacji: "java/lang/myfile.txt"
. Jeśli twoja ścieżka zaczyna się od a /
, zostanie uznana za ścieżkę bezwzględną i rozpocznie wyszukiwanie od źródła ścieżki klasy. W ten sposób wywołanie String.getResourceAsStream("/myfile.txt")
obejrzy następującą lokalizację na ścieżce zajęć ./myfile.txt
.
ClassLoader.getResourceAsStream(path)
weźmie wszystkie ścieżki za ścieżki absolutne. Więc dzwoni String.getClassLoader().getResourceAsStream("myfile.txt")
i String.getClassLoader().getResourceAsStream("/myfile.txt")
będzie zarówno wygląd pliku w ścieżce klas w następującej lokalizacji: ./myfile.txt
.
Za każdym razem, gdy wspominam o lokalizacji w tym poście, może to być lokalizacja w twoim systemie plików lub wewnątrz odpowiedniego pliku jar, w zależności od Class i / lub ClassLoadera, z którego ładujesz zasób.
W twoim przypadku ładujesz klasę z serwera aplikacji, więc powinieneś użyć Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)
zamiast this.getClass().getClassLoader().getResourceAsStream(fileName)
. this.getClass().getResourceAsStream()
będzie również działać.
Przeczytaj ten artykuł, aby uzyskać bardziej szczegółowe informacje na temat tego konkretnego problemu.
Ostrzeżenie dla użytkowników Tomcat 7 i niższych
Jedna z odpowiedzi na to pytanie mówi, że moje wyjaśnienie wydaje się niepoprawne w przypadku Tomcat 7. Próbowałem się rozejrzeć, aby zobaczyć, dlaczego tak się dzieje.
Więc spojrzałem na kod źródłowy Tomcata WebAppClassLoader
dla kilku wersji Tomcata. Implementacja findResource(String name)
(która jest całkowicie odpowiedzialna za tworzenie adresu URL do żądanego zasobu) jest praktycznie identyczna w Tomcat 6 i Tomcat 7, ale jest inna w Tomcat 8.
W wersjach 6 i 7 implementacja nie próbuje znormalizować nazwy zasobu. Oznacza to, że w tych wersjach classLoader.getResourceAsStream("/resource.txt")
może nie dawać tego samego wyniku co classLoader.getResourceAsStream("resource.txt")
zdarzenie, chociaż powinno (ponieważ tak określa Javadoc). [kod źródłowy]
Jednak w wersji 8 nazwa zasobu jest znormalizowana, aby zagwarantować, że używana jest bezwzględna wersja nazwy zasobu. Dlatego w Tomcat 8 dwa opisane powyżej połączenia powinny zawsze zwracać ten sam wynik. [kod źródłowy]
W rezultacie, trzeba być bardzo ostrożnym podczas korzystania ClassLoader.getResourceAsStream()
lub Class.getResourceAsStream()
w wersjach Tomcat wcześniej niż 8. I trzeba też pamiętać, że class.getResourceAsStream("/resource.txt")
faktycznie rozmowy classLoader.getResourceAsStream("resource.txt")
(wiodącym /
jest usuwany).
getClass().getResourceAsStream("/myfile.txt")
zachowuje się inaczej niżgetClassLoader().getResourceAsStream("/myfile.txt")
.getClassLoader()
odString
, jest to błąd lub potrzebujesz rozszerzenie?Służy
MyClass.class.getClassLoader().getResourceAsStream(path)
do ładowania zasobów powiązanych z Twoim kodem. UżyjMyClass.class.getResourceAsStream(path)
jako skrótu i do zasobów spakowanych w pakiecie klasy.Służy
Thread.currentThread().getContextClassLoader().getResourceAsStream(path)
do uzyskiwania zasobów, które są częścią kodu klienta, a nie ściśle związane z kodem wywołującym. Powinieneś być ostrożny, ponieważ moduł ładujący klasy kontekstu wątku może wskazywać na wszystko.źródło
Zwykły stary Java na zwykłym starym Java 7 i żadna inna zależność nie pokazuje różnicy ...
Włożyłem
file.txt
wc:\temp\
i umieścićc:\temp\
na ścieżce klasy.Jest tylko jeden przypadek, w którym istnieje różnica między dwoma połączeniami.
źródło
Wszystkie te odpowiedzi tutaj, a także odpowiedzi na to pytanie sugerują, że ładowanie bezwzględnych adresów URL, takich jak „/foo/bar.properties”, traktowane było tak samo przez
class.getResourceAsStream(String)
iclass.getClassLoader().getResourceAsStream(String)
. Tak NIE jest, przynajmniej nie w mojej konfiguracji / wersji Tomcat (obecnie 7.0.40).Przepraszam, nie mam absolutnie żadnego satysfakcjonującego wyjaśnienia, ale wydaje mi się, że kocur robi brudne sztuczki i swoją czarną magię z modułami ładującymi klasy i powoduje różnicę. Zawsze używałem
class.getResourceAsStream(String)
w przeszłości i nie miałem żadnych problemów.PS: Tutaj też to opublikowałem
źródło
ClassLoader.getResourceAsStream()
jako absolutnych? Jest to prawdopodobne, ponieważ, jak wspomniano w niektórych komentarzach powyżej,Class.getResourceAsStream
faktycznie wywołuje getClassLoader (). GetResourceAsStream`, ale usuwa wszelkie wiodące ukośniki.Class.getResourceAsStream()
iClassLoader.getResourceAsStream()
ostatecznie wywołujemyClassLoader.findResource()
metodę chronioną, której domyślna implementacja jest pusta, ale której javadoc wyraźnie stwierdza „Implementacje modułu ładującego klasy powinny zastąpić tę metodę, aby określić gdzie znaleźć zasoby ". Podejrzewam, że implementacja tej konkretnej metody przez Tomcat może być wadliwa.WebAppClassLoader.findResource(String name)
w Tomcat 7 z implementacją Tomcat 8 i wydaje się, że istnieje zasadnicza różnica. Tomcat 8 wyraźnie znormalizuje nazwę zasobu, dodając wiodące,/
jeśli nie zawiera, co powoduje, że wszystkie nazwy są bezwzględne. Tomcat 7 nie. To wyraźnie błąd w Tomcat 7Po wypróbowaniu kilku sposobów bezskutecznego załadowania pliku przypomniałem sobie, że mogę go użyć
FileInputStream
, co działało idealnie.Jest to kolejny sposób na odczytanie pliku do pliku
InputStream
, który odczytuje plik z aktualnie uruchomionego folderu.źródło
Działa, wypróbuj to:
źródło