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.