Bagaimana saya bisa mendapatkan "Folder" sumber daya dari dalam File jar saya?

140

Saya memiliki folder / paket sumber daya di root proyek saya, saya "tidak" ingin memuat File tertentu. Jika saya ingin memuat File tertentu, saya akan menggunakan class.getResourceAsStream dan saya akan baik-baik saja !! Apa yang sebenarnya ingin saya lakukan adalah memuat "Folder" di dalam folder sumber daya, memutar File di dalam Folder itu dan mendapatkan Stream ke setiap file dan membaca di konten ... Asumsikan bahwa nama File tidak ditentukan sebelum runtime ... Apa yang harus saya lakukan? Apakah ada cara untuk mendapatkan daftar file di dalam Folder di File jar Anda? Perhatikan bahwa file Jar dengan sumber daya adalah file jar yang sama dengan kode yang dijalankan ...

Terima kasih sebelumnya...

Mostafa Zeinali
sumber
1
Ini akan membantu: stackoverflow.com/questions/1529611/…
Jigar Joshi
Juga lihat: stackoverflow.com/questions/4764347/…
Jigar Joshi
5
Yang pertama adalah tentang mengekstrak file jar, yang merupakan hal terakhir yang ingin saya coba, seperti situasi jika-lebih buruk-datang-ke-lebih buruk !! Jika saya ingin file diekstraksi, saya akan menaruhnya di folder instalasi saya pada saat instalasi! Saya ingin mengaksesnya dari dalam jar, dan alasan saya adalah, jika getResourceAsStream dapat mengakses File, Java juga harus dapat menjelajahi folder di dalam jar itu, tanpa perlu mengekstraknya !! Tapi terima kasih ...
Mostafa Zeinali

Jawaban:

112

Akhirnya, saya menemukan solusinya:

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
        }
    }
}

Blok kedua hanya berfungsi ketika Anda menjalankan aplikasi di IDE (bukan dengan file jar), Anda dapat menghapusnya jika Anda tidak menyukainya.

pengguna1079877
sumber
Memperbarui jawaban yang benar. Meskipun ini bukan yang ingin saya lakukan, terutama dalam kode dengan 34000+ file sumber ... Tetapi tampaknya ini satu-satunya solusi. Terima kasih.
Mostafa Zeinali
4
FYI, ini tidak berfungsi jika diluncurkan dari webstart karena getPath mengembalikan sesuatu yang berhubungan dengan domain server.
amos
1
Jangan berpikir itu akan bekerja jika path ada di dalam jar, akan memberikan kesalahan ini pada konversi toURI: java.lang.IllegalArgumentException: URI tidak hierarkis
tribbloid
4
Hanya berfungsi untuk file jar tunggal, tidak berfungsi untuk mengekstrak sumber daya dari dependensi di jar lain
tribbloid
2
Ini bukan solusi yang aman, karena: 1. Menganggap file .jar adalah file lokal di sistem yang sama; 2. URL.getPath () tidak mengembalikan nama file yang valid, ini hanya mengembalikan bagian jalur dari URL, dengan semua persen-escapes utuh; 3. ProtectionDomain.getCodeSource () bisa mengembalikan null .
VGR
13

Coba berikut ini.
Jadikan jalur sumber daya "<PathRelativeToThisClassFile>/<ResourceDirectory>" Misalnya jika jalur kelas Anda adalah com.abc.package.MyClass dan file sumber Anda berada dalam 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
    }
}

Anda juga bisa menggunakan

URL url = MyClass.class.getResource("/com/abc/package/resources/");
Glen Best
sumber
27
Terima kasih atas waktu dan tenaga, tetapi ini TIDAK berfungsi ketika basis kode Anda di dalam file .jar dan sumber daya Anda juga ada di File .jar itu. Ketika Anda berada di dalam file .jar, Anda tidak dapat membuat File () dan Anda juga tidak bisa mendapatkan URL untuk folder itu ... Saya pribadi setuju dengannya dan hanya mencantumkan setiap file dalam XML ... Saya tidak ' tidak berpikir Anda dapat melakukannya untuk folder di dalam file jar sama sekali ...
Mostafa Zeinali
2
Maaf Anda mengalami masalah. Namun, ini benar-benar berfungsi ketika basis kode Anda ada di file jar dan sumber daya Anda juga ada di file jar itu. Anda tidak membuat File () pada disk - Anda membaca File di dalam file jar ke dalam memori java. Perhatikan bahwa ClassLoader dengan senang hati memperlakukan file jar yang setara dengan direktori di disk. Berikut detail dari sumbernya:
Glen Best
1
Baru saja menjalankan kode di stoples dan mendapatkan kesalahan ... Ups. V maaf, salahku. Perlu menangani jalur URL jar dengan "File: ...! / Dir1 / dir2 ...". Dua perbaikan: stackoverflow.com/questions/1429172/… ATAU String jarPath = dirURL.getPath (). Substring (5, dirURL.getPath (). IndexOf ("!")); JarFile jar = JarFile baru (URLDecoder.decode (jarPath, "UTF-8")); Pencacahan <JarEntry> entries = jar.entries (); while (entries.hasMoreElements ()) {// do something}
Glen Best
3
Dari javadoc terlihat jelas bahwa metode ini tidak SELALU dijamin untuk mengembalikan file dalam segala keadaan. TAPI, jika Anda benar-benar membaca Q, OP dijamin 100% akan bekerja dengan file dan folder berdasarkan konstruksi. Q meminta untuk mendapatkan akses ke direktori dan file - ini lebih spesifik daripada abstraksi umum dari API. Konversi ke file sudah sesuai dan benar untuk memenuhi kebutuhan. Harap baca Q dengan benar sebelum memposting komentar yang sangat bertentangan. Bersulang.
Glen Best
7
OP tidak bekerja dengan file dan direktori. Dia bekerja dengan sumber daya dalam file JAR. Sumber daya dalam file JAR bukanlah file atau direktori dan tidak dapat didaftarkan dengan Filekelas. Kode ini tidak berfungsi dengan sumber daya di dalam file JAR. Titik.
Marquis dari Lorne
8

Saya tahu ini bertahun-tahun yang lalu. Tapi hanya untuk orang lain menemukan topik ini. Apa yang dapat Anda lakukan adalah menggunakan getResourceAsStream()metode dengan jalur direktori, dan Arus input akan memiliki semua nama file dari direktori itu. Setelah itu Anda dapat menggabungkan jalur dir dengan setiap nama file dan memanggil getResourceAsStream untuk setiap file dalam satu lingkaran.

Cheng Lin
sumber
8
Ini berfungsi dengan baik kecuali jika Anda berencana untuk mengguncang segalanya, ternyata.
Tustin2121
1
Saya pikir ini harus menjadi solusi yang diterima karena bersih dan tidak memerlukan penanganan versi jar dan IDE dalam kasus terpisah. Meskipun saya tidak yakin mengapa getResource tidak menemukan folder tersebut, tetapi getResourceAsStream menemukannya.
Raghuram Onti Srinivasan
6

Saya memiliki masalah yang sama ketika saya mencoba memuat beberapa konfigurasi hadoop dari sumber daya yang dikemas dalam jar ... di IDE dan di jar (versi rilis).

Saya menemukan java.nio.file.DirectoryStreamcara terbaik untuk melakukan iterasi atas isi direktori melalui filesystem lokal dan 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());
    }
}
SQL. Injeksi
sumber
Ini bekerja dengan baik untuk saya. Hanya saya yang mengganti 'Path jarFile = Paths.get (jar.toString (). Substring ("file:". Length ()));' dengan 'Path jarFile = Paths.get (jar.toURI ());' agar dapat berfungsi di Windows. Juga menggunakan pernyataan percobaan dengan sumber daya untuk objek FileSystem.
Jarle Jacobsen
Ini berhasil untuk saya! Saya menjalankan aplikasi dropwizard di buruh pelabuhan.
nIKUNJ
4

Kode berikut mengembalikan "folder" yang diinginkan sebagai Path terlepas dari apakah itu di dalam jar atau tidak.

  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);
    }
  }

Membutuhkan java 7+.

bersenandung
sumber
Bagus! Saya harus menambahkan bahwa FileSystem is Closeable, yang berarti Anda harus menutupnya di beberapa titik untuk membebaskan sumber daya sistem. Tentu saja, Path yang dikembalikan akan menjadi tidak valid segera setelah itu.
Kirill Gamazkov
Perhatikan bahwa dalam kasus file jar, nama sumber daya (di sini paket + folder) tampaknya diabaikan saat membuat sistem file. Oleh karena getPathitu tidak kembali folder/path/to/..., tetapi hanya path/to/....
Marcono1234
1

Solusi lain, Anda bisa melakukannya dengan menggunakan ResourceLoaderseperti ini:

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())
}
Qy Zuo
sumber
0

Sederhana ... gunakan OSGi. Di OSGi Anda dapat mengulang entri Bundle Anda dengan findEntries dan findPaths.

Peter Kriens
sumber
11
Jika itu melibatkan OSGI, saya tidak akan menyebutnya 'sederhana'. Tapi kalau kebetulan Anda sudah menggunakan OSGI ... ya pasti. Tetapi menyarankan untuk pindah ke OSGI hanya untuk menyelesaikan masalah khusus ini tampaknya terlalu berlebihan.
Kris
OSGi juga memecahkan banyak masalah lain dan saat ini sangat mudah untuk memulainya. Lihat tutorial OSGi enRoute
Peter Kriens
Saya akan menjauh dari OSGi kecuali Anda sudah diharuskan untuk menggunakannya. Lebih dari Bloatware yang dirancang yang memecahkan masalah penyebaran IMO 90-an.
pengguna2337270
-1

Di dalam file jar saya, saya memiliki folder bernama Upload, folder ini memiliki tiga file teks lain di dalamnya dan saya harus memiliki folder dan file yang persis sama di luar file jar, saya menggunakan kode di bawah ini:

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);
LoveLovelyJava
sumber
1
Hai, terima kasih atas jawabannya, tetapi, Anda tahu, Anda perlu mengetahui nama setiap file di dalam folder itu saat menulis kode; dan Anda harus memberi nama masing-masing secara eksplisit. Apa yang saya cari adalah metode untuk iterasi pada file di dalam folder, dan mendapatkan nama mereka pada waktu proses, sehingga saya dapat menambahkan sumber daya ke folder, tanpa mengubah kode, mengetahui kode akan memprosesnya juga. Terima kasih atas waktu Anda. : 3
Mostafa Zeinali
-1

Seperti yang ditunjukkan jawaban lain, begitu sumber daya berada di dalam file jar, semuanya menjadi sangat buruk. Dalam kasus kami, solusi ini:

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

berfungsi sangat baik dalam pengujian (karena saat pengujian dijalankan, kode tidak dikemas dalam file jar), tetapi tidak berfungsi saat aplikasi benar-benar berjalan normal. Jadi apa yang telah saya lakukan adalah ... Saya melakukan hardcode daftar file di aplikasi, tetapi saya memiliki tes yang membaca daftar sebenarnya dari disk (dapat melakukannya karena itu berfungsi dalam tes) dan gagal jika daftar sebenarnya tidak tidak cocok dengan daftar yang dikembalikan aplikasi.

Dengan begitu saya memiliki kode sederhana di aplikasi saya (tidak ada trik), dan saya yakin saya tidak lupa menambahkan entri baru ke dalam daftar berkat pengujian.

Emmanuel Touzery
sumber
-26

Tautan ini memberi tahu Anda caranya.

Ajaibnya adalah metode getResourceAsStream ():

InputStream is = 
this.getClass().getClassLoader().getResourceAsStream("yourpackage/mypackage/myfile.xml")
James Anderson
sumber
21
Tidak kawan !! Saya TIDAK ingin satu file !! Saya ingin seluruh Folder, yang sub filenya belum ditentukan sebelum runtime !!!
Mostafa Zeinali