getResourceAsStream mengembalikan nol

183

Saya memuat file teks dari dalam sebuah paket di JAR yang dikompilasi dari proyek Java saya. Struktur direktori yang relevan adalah sebagai berikut:

/src/initialization/Lifepaths.txt

Kode saya memuat file dengan menelepon Class::getResourceAsStreamuntuk mengembalikan a InputStream.

public class Lifepaths {
    public static void execute() {
        System.out.println(Lifepaths.class.getClass().
            getResourceAsStream("/initialization/Lifepaths.txt"));
    }

    private Lifepaths() {}

    //This is temporary; will eventually be called from outside
    public static void main(String[] args) {execute();}
}

Hasil cetak akan selalu dicetak null, apa pun yang saya gunakan. Saya tidak yakin mengapa hal di atas tidak berhasil, jadi saya juga sudah mencoba:

  • "/src/initialization/Lifepaths.txt"
  • "initialization/Lifepaths.txt"
  • "Lifepaths.txt"

Tak satu pun dari ini bekerja. Saya telah membaca banyak pertanyaan sejauh ini tentang topik ini, tetapi tidak ada satupun yang membantu - biasanya, mereka hanya mengatakan untuk memuat file menggunakan path root, yang sudah saya lakukan. Itu, atau hanya memuat file dari direktori saat ini (hanya memuat filename), yang saya juga coba. File sedang dikompilasi ke dalam JAR di lokasi yang sesuai dengan nama yang sesuai.

Bagaimana saya mengatasi ini?

Basil Bourque
sumber
3
Sudahkah Anda memeriksa bahwa itu benar-benar ada dalam file jar? Sudahkah Anda memeriksa casing file?
Jon Skeet
@ JonSkeet Ini memang sedang dikompilasi ke dalam file JAR di lokasi yang sesuai, dan kasingnya sudah benar.
1
@greedybuddha Walaupun saya tidak bisa memohonnya dari konteks statis, saya bisa memintanya menggunakan Lifepaths.class. Yang sedang berkata, mengapa itu getClassLoader()memungkinkan untuk bekerja? (Juga, jangan ragu untuk mengirim jawaban!)
Bisakah kamu menunjukkan Lifepaths.getClass()? Tidak ada metode statis yang didefinisikan dalam Object ...
Puce
1
Lihatlah jawaban ini & lihat apakah Anda dapat membuatnya bekerja menggunakan getResource(String). BTW - Saya selalu memiliki masalah dalam membuat salah satu dari mereka bekerja dalam statickonteks. Masalahnya pada dasarnya bahwa kelas loader yang diperoleh adalah yang dimaksudkan untuk kelas J2SE. Anda perlu mendapatkan akses ke pemuat kelas konteks yang ditujukan untuk aplikasi itu sendiri.
Andrew Thompson

Jawaban:

159

Lifepaths.class.getClass().getResourceAsStream(...) memuat sumber daya menggunakan pemuat kelas sistem, ini jelas gagal karena tidak melihat JAR Anda

Lifepaths.class.getResourceAsStream(...) memuat sumber daya menggunakan loader kelas yang sama yang memuat kelas Lifepaths dan harus memiliki akses ke sumber daya di JAR Anda

hoaz
sumber
129
Hanya untuk menambahkan, Ketika menjalankan getResourceAsStream (nama), nama harus dimulai dengan "/". Saya tidak yakin apakah ini perlu, tetapi saya punya masalah tanpanya.
David
3
Saya telah mengacaukannya sejak jam 8 pagi ini / kemarin pagi. Menyelamatkan saya. Saya juga membutuhkan garis miring untuk membuatnya bekerja.
kyle
4
Juga perlu diingat bahwa sumber yang diinginkan dapat berada di luar hirarki paket. Dalam hal ini Anda harus menggunakan "../" di jalur Anda untuk naik satu tingkat dan kemudian turun ke cabang jalur lain untuk mencapai sumber daya Anda.
Zon
3
@ David - Saya pikir (memimpin '/') diperlukan jika tidak mencari relatif terhadap paket Lifepaths.class
Mz A
5
Hanya untuk menambahkan beberapa info, Anda perlu menambahkan /sebelum path Anda jika file Anda berada di bawah direktori yang berbeda; misalnya initialization/Lifepaths.txt. Jika path file adalah sama dengan kelas Anda (tetapi di bawah sumber daya sebagai dir utama) Anda bisa meletakkan nama file tanpa /. Sebagai contoh jika kelas Anda memiliki jalur berikut src/main/java/paths/Lifepaths.java, file Anda harus memiliki jalur ini src/main/resources/paths/Lifepaths.txt.
Dwhitz
60

Aturannya adalah sebagai berikut:

  1. periksa lokasi file yang ingin Anda muat di dalam JAR (dan dengan demikian juga pastikan itu benar-benar ditambahkan ke JAR)
  2. gunakan salah satu jalur absolut: jalur dimulai pada akar JAR
  3. gunakan jalur relatif: jalur dimulai pada direktori paket dari kelas yang Anda panggil getResource / getResoucreAsStream

Dan coba:

Lifepaths.class.getResourceAsStream("/initialization/Lifepaths.txt")

dari pada

Lifepaths.class.getClass().getResourceAsStream("/initialization/Lifepaths.txt")

(tidak yakin apakah itu membuat perbedaan, tetapi yang pertama akan menggunakan ClassLoader / JAR yang benar, sementara saya tidak yakin dengan yang terakhir)

Puce
sumber
2
Saya sudah melakukan ketiga hal ini. Baca kembali pertanyaan saya.
Dari pertanyaan Anda, tidak jelas apa "Struktur direktori yang relevan" dan jika Anda sudah benar-benar memeriksa jika dan di mana file tersebut berada di JAR (langkah 1)
Puce
1
Komentar Anda tentang jalan relatif akhirnya menyelesaikan masalah di akhir saya; Terima kasih!
Paul Bormans
Menarik. Ternyata saya harus melakukan "/config.properties" (dengan garis miring) untuk sampai ke sana ...
Erk
45

Jadi ada beberapa cara untuk mendapatkan sumber daya dari toples dan masing-masing memiliki sintaks yang sedikit berbeda di mana jalur perlu ditentukan secara berbeda.

Penjelasan terbaik yang saya lihat adalah artikel ini dari JavaWorld . Saya akan meringkas di sini, tetapi jika Anda ingin tahu lebih banyak Anda harus memeriksa artikel.

Metode

1) ClassLoader.getResourceAsStream().

Format: "/" - nama yang dipisahkan; tidak ada penuntun "/" (semua nama adalah absolut).

Contoh: this.getClass().getClassLoader().getResourceAsStream("some/pkg/resource.properties");

2) Class.getResourceAsStream()

Format: "/" - nama yang dipisahkan; memimpin "/" menunjukkan nama absolut; semua nama lain relatif terhadap paket kelas

Contoh: this.getClass().getResourceAsStream("/some/pkg/resource.properties");

greedybuddha
sumber
contoh kedua Anda rusak
Janus Troelsen
1
Contoh kedua tidak rusak, seperti yang saya katakan dalam jawaban itu tergantung pada di mana sumber daya itu berada.
greedybuddha
Juga: pastikan IDE Anda melihat file ('some / pkg / resource.properties') dengan menyegarkan folder sumber.
Eric Duminil
15

Jangan gunakan jalur absolut, buatlah itu relatif terhadap direktori 'sumber daya' di proyek Anda. Kode cepat dan kotor yang menampilkan konten MyTest.txt dari direktori 'resources'.

@Test
public void testDefaultResource() {
    // can we see default resources
    BufferedInputStream result = (BufferedInputStream) 
         Config.class.getClassLoader().getResourceAsStream("MyTest.txt");
    byte [] b = new byte[256];
    int val = 0;
    String txt = null;
    do {
        try {
            val = result.read(b);
            if (val > 0) {
                txt += new String(b, 0, val);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } 
    } while (val > -1);
    System.out.println(txt);
}
Paul Smith
sumber
9

Anda mungkin ingin mencoba ini untuk mendapatkan aliran yaitu pertama mendapatkan url dan kemudian membukanya sebagai aliran.

URL url = getClass().getResource("/initialization/Lifepaths.txt"); 
InputStream strm = url.openStream(); 

Saya pernah memiliki pertanyaan serupa: Membaca file txt dari jar gagal tetapi membaca gambar berhasil

Tn
sumber
3
inilah yang dilakukan getResourceAsStream ()
fedeb
8

Tampaknya ada masalah dengan ClassLoader yang Anda gunakan. Gunakan contextClassLoader untuk memuat kelas. Ini terlepas dari apakah itu dalam metode statis / non-statis

Thread.currentThread().getContextClassLoader().getResourceAsStream......

Binita Bharati
sumber
6

Saya menemukan diri saya dalam masalah serupa. Karena saya menggunakan maven, saya perlu memperbarui pom.xml saya untuk memasukkan sesuatu seperti ini:

   ...
</dependencies>
<build>
    <resources>
        <resource>
            <directory>/src/main/resources</directory>
        </resource>
        <resource>
            <directory>../src/main/resources</directory>
        </resource>
    </resources>
    <pluginManagement>
        ...

Perhatikan tag sumber daya di sana untuk menentukan di mana folder itu berada. Jika Anda memiliki proyek bersarang (seperti saya lakukan) maka Anda mungkin ingin mendapatkan sumber daya dari daerah lain, bukan hanya dalam modul yang Anda kerjakan. Ini membantu mengurangi menyimpan file yang sama di setiap repo jika Anda menggunakan data konfigurasi yang sama

plosco
sumber
3

Apa yang berhasil bagi saya adalah menambahkan file di bawah My Project/Java Resources/srcdan kemudian gunakan

this.getClass().getClassLoader().getResourceAsStream("myfile.txt");

Saya tidak perlu menambahkan file ini secara eksplisit ke path (menambahkannya untuk /srcmelakukannya rupanya)

CommonCoreTawan
sumber
Sintaks java salah
Ilya Kharlamov
2

Tidak tahu apakah bisa membantu, tetapi dalam kasus saya, saya memiliki sumber daya di folder / src / dan mendapatkan kesalahan ini. Saya kemudian memindahkan gambar ke folder bin dan itu memperbaiki masalah.

Leo Ufimtsev
sumber
2

Pastikan direktori sumber daya Anda (misalnya "src") ada di classpath Anda (pastikan direktori sumber di path build Anda di gerhana).

Pastikan clazz diambil dari classloader utama.

Kemudian, untuk memuat src / inisialisasi / Lifepaths.txt, gunakan

clazz.getResourceAsStream("/initialization/Lifepaths.txt");

Why: clazz.getResourcesAsStream(foo)mencari foo dari dalam classpath clazz , relatif terhadap direktori clazz tinggal . "/" Yang memimpin membuatnya memuat dari root direktori apa pun di classpath of clazz.

Kecuali Anda berada dalam suatu wadah, seperti Tomcat, atau sedang melakukan sesuatu dengan ClassLoaders secara langsung, Anda bisa memperlakukan ecpathse / command line classpath sebagai satu-satunya classpath classpath.

Bobby Martin
sumber
2

Default JVM classloader akan menggunakan induk-classloader untuk memuat sumber pertama: deletegate-parent-classloader.

Lifepaths.class.getClass()classloader adalah bootstrap classloader, jadi getResourceAsStreamakan mencari hanya $ JAVA_HOME, terlepas dari pengguna yang disediakanclasspath . Jelas, Lifepaths.txt tidak ada di sana.

Lifepaths.classclassloader adalah system classpath classloader, jadi getResourceAsStreamakan mencari yang ditentukan pengguna classpathdan Lifepaths.txt ada di sana.

Saat menggunakan java.lang.Class#getResourceAsStream(String name), nama yang tidak dimulai dengan '/' akan ditambahkan dengan package nameawalan. Jika Anda ingin menghindari ini, silakan gunakan java.lang.ClassLoader#getResourceAsStream. Sebagai contoh:

ClassLoader loader = Thread.currentThread().getContextClassLoader();
String resourceName = "Lifepaths.txt";
InputStream resourceStream = loader.getResourceAsStream(resourceName); 
ridox
sumber
2

Secara kasar:

getClass().getResource("/") ~ = Thread.currentThread().getContextClassLoader().getResource(".")

Misalkan struktur proyek Anda seperti berikut:

├── src
   ├── main
   └── test
   └── test
       ├── java
          └── com
              └── github
                  └── xyz
                      └── proj
                          ├── MainTest.java
                          └── TestBase.java
       └── resources
           └── abcd.txt
└── target
    └── test-classes
        ├── com
        └── abcd.txt
// in MainClass.java
this.getClass.getResource("/") -> "~/proj_dir/target/test-classes/"
this.getClass.getResource(".") -> "~/proj_dir/target/test-classes/com/github/xyz/proj/"
Thread.currentThread().getContextClassLoader().getResources(".") -> "~/proj_dir/target/test-classes/"
Thread.currentThread().getContextClassLoader().getResources("/") ->  null
Shijing Lv
sumber
2

jika Anda menggunakan Maven, pastikan kemasan Anda adalah 'toples' bukan 'pom'.

<packaging>jar</packaging>
Feku279
sumber
0

Yang Anda butuhkan adalah classPath absolut penuh untuk file tersebut. Jadi alih-alih menebaknya, coba cari tahu ROOT dan kemudian pindahkan file ke basis file yang lebih baik <.war> struktur file ...

URL test1 = getClass().getResource("/");
URL test2 = getClass().getClassLoader().getResource("/");            
URL test3 = getClass().getClassLoader().getResource("../");

logger.info(test1.getPath()); 
logger.info(test2.getPath());
logger.info(test3.getPath());
ConAim
sumber
0

Apa yang berhasil bagi saya adalah saya meletakkan file di bawah

src/main/java/myfile.log

dan

InputStream is = getClass().getClassLoader().getResourceAsStream("myfile.log");
        
        if (is == null) {
            throw new FileNotFoundException("Log file not provided");
        }
Akash Yellappa
sumber
Folder sumber dipanggil src/main/JAVA, jelas file non-kode Anda tidak boleh berada di sini.
leyren
-12

@Emracool ... Saya sarankan Anda alternatif. Karena Anda tampaknya mencoba memuat file * .txt. Lebih baik menggunakan FileInputStream()daripada ini getClass().getClassLoader().getResourceAsStream()atau menjengkelkan getClass().getResourceAsStream(). Setidaknya kode Anda akan dieksekusi dengan benar.

Jain
sumber
apa ??? -1 untuk jawaban yang berfungsi. Apa pun yang terjadi. Tetapi solusi yang disarankan di atas pasti akan berhasil.
Jain