Jak mogę uzyskać zasób „Folder” z wnętrza mojego pliku jar?


139

Mam folder / pakiet zasobów w katalogu głównym mojego projektu, „nie” chcę ładować określonego pliku. Gdybym chciał załadować określony plik, użyłbym class.getResourceAsStream i byłoby dobrze !! W rzeczywistości chcę załadować „Folder” w folderze zasobów, zapętlić pliki w tym folderze i pobrać strumień do każdego pliku i wczytać zawartość ... Załóżmy, że nazwy plików nie są określane przed uruchomieniem ... Co powinienem zrobić? Czy istnieje sposób, aby uzyskać listę plików znajdujących się w folderze w pliku jar? Zauważ, że plik JAR z zasobami to ten sam plik jar, z którego jest uruchamiany kod ...

Z góry dziękuję...




5
Pierwsza dotyczy wyodrębnienia pliku jar, co jest ostatnią rzeczą, jakiej chcę spróbować, na przykład sytuacja, gdy sytuacja jest gorsza! Jeśli chcę, aby pliki zostały wyodrębnione, po prostu umieściłbym je w folderze instalacyjnym podczas instalacji! Chcę uzyskać do nich dostęp z wnętrza słoika, a moim powodem jest to, że jeśli getResourceAsStream może uzyskać dostęp do pliku, Java powinna również mieć możliwość eksplorowania folderów w tym słoiku bez potrzeby wyodrębniania go !! Ale dzięki ...
— Mostafa Zeinali

Odpowiedzi:


111

Wreszcie znalazłem rozwiązanie:

final String path = "sample/folder";
final File jarFile = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath());

if(jarFile.isFile()) {  // Run with JAR file
    final JarFile jar = new JarFile(jarFile);
    final Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
    while(entries.hasMoreElements()) {
        final String name = entries.nextElement().getName();
        if (name.startsWith(path + "/")) { //filter according to the path
            System.out.println(name);
        }
    }
    jar.close();
} else { // Run with IDE
    final URL url = Launcher.class.getResource("/" + path);
    if (url != null) {
        try {
            final File apps = new File(url.toURI());
            for (File app : apps.listFiles()) {
                System.out.println(app);
            }
        } catch (URISyntaxException ex) {
            // never happens
        }
    }
}

Drugi blok działa tylko wtedy, gdy uruchomisz aplikację na IDE (nie z plikiem jar), możesz go usunąć, jeśli ci się nie podoba.


Zaktualizowano poprawną odpowiedź. Chociaż nie jest to to, co chciałbym robić, szczególnie w przypadku kodu zawierającego ponad 34000 plików źródłowych ... Ale wydaje się, że jest to jedyne rozwiązanie. Dziękuję Ci.
— Mostafa Zeinali

4
FYI, to nie działa, jeśli jest uruchamiane z webstartu, ponieważ getPath zwraca coś odnoszącego się do domeny serwera.
— amos

1
Nie myśl, że to zadziała, jeśli ścieżka jest w słoiku, wyświetli ten błąd podczas konwersji toURI: java.lang.IllegalArgumentException: URI nie jest hierarchiczne
— tribbloid

4
Działa tylko dla pojedynczego pliku jar, nie działa w celu wyodrębnienia zasobów z zależności w innym
— pliku

2
Nie jest to bezpieczne rozwiązanie, ponieważ: 1. Zakłada, że ​​plik .jar jest plikiem lokalnym w tym samym systemie; 2. URL.getPath () nie zwraca prawidłowej nazwy pliku, po prostu zwraca część adresu URL zawierającą ścieżkę, z nienaruszonymi wszystkimi znakami ucieczki procent; 3. ProtectionDomain.getCodeSource () może zwrócić wartość null .
— VGR

14

Spróbuj wykonać następujące czynności.
Utwórz ścieżkę do zasobu "<PathRelativeToThisClassFile>/<ResourceDirectory>" Np. Jeśli ścieżka klasy to com.abc.package.MyClass, a pliki zasobów znajdują się w src / com / abc / package / resources /:

URL url = MyClass.class.getResource("resources/");
if (url == null) {
     // error - missing folder
} else {
    File dir = new File(url.toURI());
    for (File nextFile : dir.listFiles()) {
        // Do something with nextFile
    }
}

Możesz także użyć

URL url = MyClass.class.getResource("/com/abc/package/resources/");

27
Dziękuję za czas i wysiłek, ale to NIE działa, gdy twój kod jest zapisany w pliku .jar, a twoje zasoby są również w tym pliku .jar. Kiedy jesteś w pliku .jar, nie możesz utworzyć pliku (), a także nie możesz uzyskać adresu URL dla tego folderu ... Osobiście pogodziłem się z tym i po prostu wymieniłem każdy plik w XML ... Nie ” myślę, że w ogóle możesz to zrobić dla folderu wewnątrz pliku jar ...
— Mostafa Zeinali,

2
Przepraszam, że masz problemy. Jednak faktycznie działa, gdy baza kodu znajduje się w pliku jar, a zasoby są również w tym pliku jar. Nie tworzysz File () na dysku - czytasz plik z pliku jar do pamięci java. Zauważ, że ClassLoader z przyjemnością traktuje plik jar jako odpowiednik katalogu na dysku. Oto szczegóły ze źródła:
— Glen Best,

1
Po prostu uruchomiłem kod na słoiku i otrzymałem błąd ... Ups. V przepraszam, moja wina. Musisz obsłużyć ścieżkę URL jar z "Plik: ...! / Dir1 / dir2 ...". Dwie poprawki: stackoverflow.com/questions/1429172/… LUB String jarPath = dirURL.getPath (). Substring (5, dirURL.getPath (). IndexOf ("!")); JarFile jar = nowy JarFile (URLDecoder.decode (jarPath, "UTF-8")); Wyliczenie <JarEntry> wpisów = jar.entries (); while (wpisów.hasMoreElements ()) {// zrób coś}
— Glen Best,

3
Z javadoc wynika, że ​​metoda nie ZAWSZE zwróci plik w każdych okolicznościach. ALE, jeśli faktycznie czytasz Q, OP ma 100% gwarancję, że będzie pracował z plikami i folderami według konstrukcji. Q prosi o dostęp do katalogów i plików - jest to bardziej szczegółowe niż uogólniona abstrakcja API. Konwersja do pliku jest odpowiednia i poprawna, aby sprostać potrzebom. Prosimy o dokładne przeczytanie Q przed opublikowaniem tak silnie sprzecznych komentarzy. Twoje zdrowie.
— Glen Best

7
OP nie działa z plikami i katalogami. Pracuje z zasobami w pliku JAR. Zasoby w pliku JAR nie są plikami ani katalogami i nie mogą być wymienione w Fileklasie. Ten kod nie działa z zasobami w pliku JAR. Kropka.
— Markiz Lorne

7

Wiem, że to było wiele lat temu. Ale właśnie dla innych ludzi ten temat trafia. To, co możesz zrobić, to użyć getResourceAsStream()metody ze ścieżką do katalogu, a strumień wejściowy będzie miał wszystkie nazwy plików z tego katalogu. Następnie możesz połączyć ścieżkę dir z każdą nazwą pliku i wywołać getResourceAsStream dla każdego pliku w pętli.


7
To działa dobrze, chyba że planujesz zepsuć rzeczy, jak się okazuje.
— Tustin2121,

1
Myślę, że powinno to być przyjęte rozwiązanie, ponieważ jest czyste i nie wymaga obsługi wersji jar i IDE w osobnych przypadkach. Chociaż nie jestem pewien, dlaczego getResource nie znajduje folderu, ale getResourceAsStream to robi.
— Raghuram Onti Srinivasan

6

Miałem ten sam problem, gdy próbowałem załadować niektóre konfiguracje hadoopów z zasobów zapakowanych w słoiku ... zarówno w IDE, jak i na jar (wersja wydana).

Zauważyłem, java.nio.file.DirectoryStreamże najlepiej działa iteracja po zawartości katalogów zarówno w lokalnym systemie plików, jak i jar.

String fooFolder = "/foo/folder";
....

ClassLoader classLoader = foofClass.class.getClassLoader();
try {
    uri = classLoader.getResource(fooFolder).toURI();
} catch (URISyntaxException e) {
    throw new FooException(e.getMessage());
} catch (NullPointerException e){
    throw new FooException(e.getMessage());
}

if(uri == null){
    throw new FooException("something is wrong directory or files missing");
}

/** i want to know if i am inside the jar or working on the IDE*/
if(uri.getScheme().contains("jar")){
    /** jar case */
    try{
        URL jar = FooClass.class.getProtectionDomain().getCodeSource().getLocation();
        //jar.toString() begins with file:
        //i want to trim it out...
        Path jarFile = Paths.get(jar.toString().substring("file:".length()));
        FileSystem fs = FileSystems.newFileSystem(jarFile, null);
        DirectoryStream<Path> directoryStream = Files.newDirectoryStream(fs.getPath(fooFolder));
        for(Path p: directoryStream){
            InputStream is = FooClass.class.getResourceAsStream(p.toString()) ;
        performFooOverInputStream(is);
        /** your logic here **/
            }
    }catch(IOException e) {
        throw new FooException(e.getMessage());     
    }
}
else{
    /** IDE case */
    Path path = Paths.get(uri);
    try {
        DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path);
        for(Path p : directoryStream){
            InputStream is = new FileInputStream(p.toFile());
            performFooOverInputStream(is);
        }
    } catch (IOException _e) {
        throw new FooException(_e.getMessage());
    }
}

To działało dobrze dla mnie. Tylko ja przełączyłem 'Path jarFile = Paths.get (jar.toString (). Substring ("file:". Length ()));' z 'Path jarFile = Paths.get (jar.toURI ());' aby działał w systemie Windows. Użyto również próby z instrukcją zasobów dla obiektu FileSystem.
— Jarle Jacobsen

To zadziałało dla mnie! Uruchomiłem aplikację dropwizard w dockerze.
— nIKUNJ

4

Poniższy kod zwraca żądany „folder” jako ścieżkę, niezależnie od tego, czy znajduje się w słoiku, czy nie.

  private Path getFolderPath() throws URISyntaxException, IOException {
    URI uri = getClass().getClassLoader().getResource("folder").toURI();
    if ("jar".equals(uri.getScheme())) {
      FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap(), null);
      return fileSystem.getPath("path/to/folder/inside/jar");
    } else {
      return Paths.get(uri);
    }
  }

Wymaga java 7+.


Miły! Muszę jednak dodać, że FileSystem jest Closeable, co oznacza, że ​​musisz go zamknąć w pewnym momencie, aby zwolnić zasoby systemowe. Oczywiście zwrócona ścieżka stanie się nieważna natychmiast po tym.
— Kirill Gamazkov

Zauważ, że w przypadku pliku jar nazwa zasobu (tutaj pakiet + folder) wydaje się być ignorowana podczas tworzenia systemu plików. Dlatego getPathnie wraca folder/path/to/..., ale tylko path/to/....
— Marcono1234

1

Inne rozwiązanie, możesz to zrobić za pomocą ResourceLoadertego:

import org.springframework.core.io.Resource;
import org.apache.commons.io.FileUtils;

@Autowire
private ResourceLoader resourceLoader;

...

Resource resource = resourceLoader.getResource("classpath:/path/to/you/dir");
File file = resource.getFile();
Iterator<File> fi = FileUtils.iterateFiles(file, null, true);
while(fi.hasNext()) {
    load(fi.next())
}

0

Proste ... użyj OSGi. W OSGi możesz iterować po wpisach swojego Pakietu za pomocą funkcji findEntries i findPaths.


10
Jeśli chodzi o OSGI, nie nazwałbym tego „prostym”. Ale jeśli już używasz OSGI ... tak, jasne. Jednak sugerowanie przejścia do OSGI tylko po to, aby rozwiązać ten konkretny problem, wydaje się zbyt trudne.
— Kris

OSGi rozwiązuje również wiele innych problemów i w dzisiejszych czasach bardzo łatwo jest zacząć. Zobacz samouczki OSGi enRoute
— Peter Kriens

Trzymałbym się z daleka od OSGi, chyba że już musisz go używać. Przebudowane oprogramowanie typu Bloatware, które rozwiązuje problemy związane z wdrażaniem IMO z lat 90.
— user2337270

-1

W moim pliku jar miałem folder o nazwie Upload, ten folder zawierał trzy inne pliki tekstowe i potrzebowałem mieć dokładnie ten sam folder i pliki poza plikiem jar, użyłem poniższego kodu:

URL inputUrl = getClass().getResource("/upload/blabla1.txt");
File dest1 = new File("upload/blabla1.txt");
FileUtils.copyURLToFile(inputUrl, dest1);

URL inputUrl2 = getClass().getResource("/upload/blabla2.txt");
File dest2 = new File("upload/blabla2.txt");
FileUtils.copyURLToFile(inputUrl2, dest2);

URL inputUrl3 = getClass().getResource("/upload/blabla3.txt");
File dest3 = new File("upload/Bblabla3.txt");
FileUtils.copyURLToFile(inputUrl3, dest3);

1
Cześć, dziękuję za odpowiedź, ale widzisz, podczas pisania kodu trzeba było znać nazwę każdego pliku w tym folderze; i trzeba było wyraźnie nazwać każdy z nich. To, czego szukałem, to metoda iteracji plików w folderze i uzyskiwania ich nazw w czasie wykonywania, aby móc dodawać zasoby do folderu bez zmiany kodu, wiedząc, że kod również je przetworzy. Dziękuję za poświęcony czas. : 3
— Mostafa Zeinali

-1

Jak wskazują inne odpowiedzi, gdy zasoby znajdą się w pliku jar, sprawy stają się naprawdę brzydkie. W naszym przypadku to rozwiązanie:

https://stackoverflow.com/a/13227570/516188

działa bardzo dobrze w testach (ponieważ po uruchomieniu testów kod nie jest spakowany w pliku jar), ale nie działa, gdy aplikacja faktycznie działa normalnie. Więc to, co zrobiłem, to ... Zakodowałem listę plików w aplikacji, ale mam test, który czyta aktualną listę z dysku (mogę to zrobić, ponieważ działa w testach) i kończy się niepowodzeniem, jeśli rzeczywista lista nie działa nie pasuje do listy zwracanej przez aplikację.

W ten sposób mam prosty kod w mojej aplikacji (bez sztuczek) i jestem pewien, że dzięki testowi nie zapomniałem dodać nowego wpisu do listy.


-25

Ten link zawiera informacje, jak to zrobić.

Magią jest metoda getResourceAsStream ():

InputStream is = 
this.getClass().getClassLoader().getResourceAsStream("yourpackage/mypackage/myfile.xml")

20
Nie koleś !! NIE chcę jednego pliku !! Chcę mieć cały folder, którego pliki podrzędne są nieokreślone przed uruchomieniem !!!
— Mostafa Zeinali
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.