otwarty zasób ze ścieżką względną w Javie

84

W mojej aplikacji Java potrzebuję plików i katalogów.

Oto struktura programu:

./main.java
./package1/guiclass.java
./package1/resources/resourcesloader.java
./package1/resources/repository/modules/   -> this is the dir I need to get
./package1/resources/repository/SSL-Key/cert.jks    -> this is the file I need to get

guiclass ładuje klasę resourcesloader, która załaduje moje zasoby (katalog i plik).

Co do akt, próbowałem

resourcesloader.class.getClass().getResource("repository/SSL-Key/cert.jks").toString()

aby uzyskać prawdziwą ścieżkę, ale ta droga nie działa.

Nie mam pojęcia, której ścieżki użyć do katalogu.

Giancarlo
źródło
1
class.getClass () to nie to samo co class.getClassLoader (). Jest też inne rozwiązanie, getResourceAsStream () używające klasy w tym samym pakiecie co zasób. Więcej informacji: tshikatshikaaa.blogspot.nl/2012/07/… .
Jérôme Verstrynge

Odpowiedzi:

48

Podaj ścieżkę względem modułu ładującego klasy, a nie klasy, z której otrzymujesz moduł ładujący. Na przykład:

resourcesloader.class.getClassLoader().getResource("package1/resources/repository/SSL-Key/cert.jks").toString();
jonathan.cone
źródło
161

Miałem problemy ze stosowaniem getClass().getResource("filename.txt")metody. Po przeczytaniu instrukcji Java Docs, jeśli zasób nie znajduje się w tym samym pakiecie co klasa, z której próbujesz uzyskać dostęp do zasobu, musisz podać mu ścieżkę względną zaczynającą się od '/'. Zalecaną strategią jest umieszczenie plików zasobów w folderze „resources” w katalogu głównym. Na przykład, jeśli masz strukturę:

src/main/com/mycompany/myapp

następnie możesz dodać folder zasobów zgodnie z zaleceniami maven w:

src/main/resources

ponadto możesz dodać podfoldery w folderze zasobów

src/main/resources/textfiles

i powiedz, że twój plik nazywa się myfile.txttak

src/main/resources/textfiles/myfile.txt

Tutaj pojawia się problem z głupią ścieżką. Załóżmy, że masz klasę w swojej com.mycompany.myapp packagei chcesz uzyskać dostęp do myfile.txtpliku z folderu zasobów. Niektórzy mówią, że musisz podać:

"/main/resources/textfiles/myfile.txt" path

lub

"/resources/textfiles/myfile.txt"

oba są błędne. Po uruchomieniu mvn clean compilepliki i foldery są kopiowane w:

myapp/target/classes 

teczka. Ale folderu zasobów nie ma, tylko foldery w folderze zasobów. Więc masz:

myapp/target/classes/textfiles/myfile.txt

myapp/target/classes/com/mycompany/myapp/*

więc prawidłowa ścieżka do getClass().getResource("")metody to:

"/textfiles/myfile.txt"

tutaj jest:

getClass().getResource("/textfiles/myfile.txt")

To nie będzie już zwracać wartości null, ale zwróci Twoją klasę. Mam nadzieję, że to komuś pomoże. Dziwne jest dla mnie, że "resources"folder również nie jest kopiowany, a jedynie podfoldery i pliki bezpośrednio w "resources"folderze. Wydawałoby mi się logiczne, że "resources"folder byłby również znaleziony pod"myapp/target/classes"

Jasio
źródło
1
W moim target/classeswidzę tylko pliki w zasobach głównych, a nie zasoby testowe. Czemu?
Ava
@Ava Dodaj folder testowy jako folder źródłowy i gotowe!
Aakash Parashar
@Ava, z pewnością jest to późna odpowiedź, ale może komuś pomóc. Musisz wykonać mvn clean test-compilepolecenie, tak jak dzieje się to compilew cyklu życia Mavena . To wygeneruje target/test-classes.
Alexander Radchenko
34

W nadziei na dostarczenie dodatkowych informacji tym, którzy nie rozumieją tego tak szybko, jak inni, chciałbym przedstawić mój scenariusz, ponieważ ma on nieco inną konfigurację. Mój projekt został skonfigurowany z następującą strukturą katalogów (przy użyciu Eclipse):

Projekt/
  src / // kod źródłowy aplikacji
    org /
      mój projekt/
        MyClass.java
  test / // testy jednostkowe
  res / // zasoby
    images / // Obrazy PNG dla ikon
      my-image.png
    xml / // XSD do sprawdzania poprawności plików XML za pomocą JAXB
      my-schema.xsd
    conf / // domyślny plik .conf dla Log4j
      log4j.conf
  Biblioteki lib / // dodane do ścieżki kompilacji za pośrednictwem ustawień projektu

Wystąpiły problemy podczas ładowania zasobów z katalogu res . Chciałem, aby wszystkie moje zasoby były oddzielone od kodu źródłowego (po prostu do celów zarządzania / organizacji). Musiałem więc dodać katalog res do ścieżki kompilacji, a następnie uzyskać dostęp do zasobu przez:

static final ClassLoader loader = MyClass.class.getClassLoader();

// in some function
loader.getResource("images/my-image.png");
loader.getResource("xml/my-schema.xsd");
loader.getResource("conf/log4j.conf");

UWAGA:/ pominięto od początku łańcucha zasobów, ponieważ używam ClassLoader.getResource (String) zamiast Class.getResource (String) .

Bogate w e
źródło
1
. +1 za zasugerowanie ClassLoader
pyb
1
Najlepsza odpowiedź na ten temat! Wskazówka dotycząca nieużywania /na początku ścieżki zasobów była kluczowa!
Kayhadrin
10

Kiedy używasz „getResource” w klasie, ścieżka względna jest rozpoznawana na podstawie pakietu, w którym znajduje się klasa. Gdy używasz „getResource” na ClassLoader, ścieżka względna jest rozpoznawana na podstawie folderu głównego.

Jeśli używasz ścieżki bezwzględnej, obie metody „getResource” zostaną uruchomione w folderze głównym.

nbeyer
źródło
Nie zapomnij o efektach/
Gilberto,
@nbeyer Dlaczego używanie getResourceze ścieżką bezwzględną rozpoczyna się w folderze głównym? Czy cel absolutnej ścieżki nie jest absolutny ?
atjua,
10

@GianCarlo: Możesz spróbować wywołać właściwość systemową user.dir, która da ci katalog główny twojego projektu java, a następnie dodaj tę ścieżkę do ścieżki względnej, na przykład:

String root = System.getProperty("user.dir");
String filepath = "/path/to/yourfile.txt"; // in case of Windows: "\\path \\to\\yourfile.txt
String abspath = root+filepath;



// using above path read your file into byte []
File file = new File(abspath);
FileInputStream fis = new FileInputStream(file);
byte []filebytes = new byte[(int)file.length()];
fis.read(filebytes);
Vikram
źródło
1
OP chciał zlokalizować zasób w ścieżce klas (najprawdopodobniej w słoiku wygenerowanym podczas budowania). Ta metoda nie zapewnia sposobu na osiągnięcie tego celu - nie można wskazać a Filedo zasobu w pliku jar, a jedynie do odpowiedniego wpisu w systemie plików (np. Sam jar).
Attila,
Podoba mi się pomysł uzyskania pliku „user.dir”, aby zobaczyć żądaną ścieżkę. Dzięki
YouAreAwesome
5

Dla osób używających eclipse + maven. Powiedzmy, że próbujesz uzyskać dostęp do pliku images/pic.jpgw formacie src/main/resources. Robiąc to w ten sposób:

ClassLoader loader = MyClass.class.getClassLoader();
File file = new File(loader.getResource("images/pic.jpg").getFile());

jest całkowicie poprawny, ale może spowodować wyjątek wskaźnika zerowego. Wygląda na to, że eclipse nie rozpoznaje od razu folderów w strukturze katalogów maven jako foldery źródłowe. Usuwając src/main/resourcesfolder z listy folderów źródłowych projektu i umieszczając go z powrotem (projekt> właściwości> ścieżka budowania java> źródło> usuń / dodaj folder) udało mi się to rozwiązać.

Paul Mira
źródło
2
resourcesloader.class.getClass()

Można podzielić na:

Class<resourcesloader> clazz = resourceloader.class;
Class<Class> classClass = clazz.getClass();

Co oznacza, że ​​próbujesz załadować zasób za pomocą klasy ładowania początkowego.

Zamiast tego prawdopodobnie chcesz coś takiego:

resourcesloader.class.getResource("repository/SSL-Key/cert.jks").toString()

Gdyby tylko javac ostrzegał przed wywoływaniem metod statycznych w kontekstach niestatycznych ...

Tom Hawtin - haczyk
źródło
1

Czy wykonujesz następującą pracę?

resourcesloader.class.getClass().getResource("/package1/resources/repository/SSL-Key/cert.jks")

Czy jest jakiś powód, dla którego nie możesz określić pełnej ścieżki łącznie z pakietem?

RichH
źródło
1

Idąc z dwiema odpowiedziami, jak wspomniano powyżej. Pierwszy

resourcesloader.class.getClassLoader().getResource("package1/resources/repository/SSL-Key/cert.jks").toString();
resourcesloader.class.getResource("repository/SSL-Key/cert.jks").toString()

Czy powinno być jedno i to samo?

manocha_ak
źródło
1

Aby uzyskać prawdziwą ścieżkę do pliku możesz spróbować tego:

URL fileUrl = Resourceloader.class.getResource("resources/repository/SSL-Key/cert.jks");
String pathToClass = fileUrl.getPath;    

Resourceloader to tutaj nazwa klasy. „zasoby / repozytorium / klucz-SSL / cert.jks” to ścieżka względna do pliku. Gdybyś miał swoją guiclass w ./package1/java z resztą struktury folderów, wziąłbyś "../resources/repository/SSL-Key/cert.jks" jako ścieżkę względną ze względu na reguły definiujące ścieżkę względną.

W ten sposób możesz odczytać swój plik za pomocą BufferedReader. NIE UŻYWAJ ŁAŃCUCHA do identyfikacji ścieżki do pliku, ponieważ jeśli w ścieżce znajdują się spacje lub znaki z innego alfabetu niż angielski, wystąpią problemy i plik nie zostanie znaleziony.

BufferedReader bufferedReader = new BufferedReader(
                        new InputStreamReader(fileUrl.openStream()));
Nikto
źródło
0

Zrobiłem małą modyfikację w jednym linijce @ jonathan.cone (dodając .getFile()), aby uniknąć wyjątku pustego wskaźnika i ustawiając ścieżkę do katalogu danych. Oto, co zadziałało dla mnie:

String realmID = new java.util.Scanner(new java.io.File(RandomDataGenerator.class.getClassLoader().getResource("data/aa-qa-id.csv").getFile().toString())).next();
Alferd Nobel
źródło
0

Użyj tego:

resourcesloader.class.getClassLoader().getResource("/path/to/file").**getPath();**
Oleg Poltoratskii
źródło