Membaca file sumber dari dalam toples

242

Saya ingin membaca sumber dari dalam toples saya seperti ini:

File file;
file = new File(getClass().getResource("/file.txt").toURI());
BufferredReader reader = new BufferedReader(new FileReader(file));

//Read the file

dan itu berfungsi dengan baik ketika menjalankannya di Eclipse, tetapi jika saya mengekspornya ke stoples itu menjalankannya ada IllegalArgumentException:

Exception in thread "Thread-2"
java.lang.IllegalArgumentException: URI is not hierarchical

dan saya benar-benar tidak tahu mengapa tetapi dengan beberapa pengujian saya menemukan jika saya berubah

file = new File(getClass().getResource("/file.txt").toURI());

untuk

file = new File(getClass().getResource("/folder/file.txt").toURI());

maka itu bekerja sebaliknya (itu bekerja di toples tetapi tidak gerhana).

Saya menggunakan Eclipse dan folder dengan file saya ada di folder kelas.

PrinceCJC
sumber
Jika Anda ingin membaca file dari direktori dalam toples dengan nomor berapa pun untuk file, lihat Stackoverflow-Link
Michael Hegner
1
Saya tidak yakin bahwa pertanyaan awal melibatkan Spring. Tautan dalam komentar sebelumnya merujuk ke jawaban khusus Spring dari pertanyaan yang berbeda. Saya percaya getResourceAsStreammasih solusi sederhana dan lebih portabel untuk masalah ini.
Drew MacInnis

Jawaban:

398

Daripada mencoba menangani sumber daya sebagai File, minta ClassLoader untuk mengembalikan InputStream untuk sumber daya alih-alih melalui getResourceAsStream :

InputStream in = getClass().getResourceAsStream("/file.txt"); 
BufferedReader reader = new BufferedReader(new InputStreamReader(in));

Selama file.txtsumber daya tersedia di classpath maka pendekatan ini akan bekerja dengan cara yang sama terlepas dari apakah file.txtsumber daya ada di classes/direktori atau di dalam a jar.

The URI is not hierarchicalterjadi karena URI untuk sumber daya dalam file jar akan terlihat seperti ini: file:/example.jar!/file.txt. Anda tidak dapat membaca entri dalam jar( zipfile) seperti itu adalah File lama biasa .

Ini dijelaskan dengan baik oleh jawaban untuk:

Drew MacInnis
sumber
3
Terima kasih, ini sangat membantu dan kodenya berfungsi dengan baik, tetapi saya memiliki satu masalah, saya perlu menentukan apakah InputStreamada (seperti File.exists()) sehingga game saya dapat mengetahui apakah akan menggunakan file default atau tidak. Terima kasih.
PrinceCJC
1
Oh dan BTW alasan getClass().getResource("**/folder**/file.txt")membuatnya berfungsi adalah karena saya punya folder itu di direktori yang sama dengan toples saya :).
PrinceCJC
5
getResourceAsStreammengembalikan nol jika sumber daya tidak ada sehingga bisa menjadi tes "ada" Anda.
Drew MacInnis
1
BTW, Anda memiliki salah ketik: harus BufferedReader, bukan BufferredReader (perhatikan tambahan 'r' di kemudian hari)
mailmindlin
2
Dan tentu saja ... jangan lupa untuk menutup inputStream dan BufferedReader
Noremac
26

Untuk mengakses file dalam toples Anda memiliki dua opsi:

  • Tempatkan file dalam struktur direktori yang cocok dengan nama paket Anda (setelah mengekstrak file .jar, itu harus berada di direktori yang sama dengan file .class), kemudian mengaksesnya menggunakan getClass().getResourceAsStream("file.txt")

  • Tempatkan file di root (setelah mengekstrak file .jar, itu harus di root), lalu akses menggunakan Thread.currentThread().getContextClassLoader().getResourceAsStream("file.txt")

Opsi pertama mungkin tidak berfungsi ketika jar digunakan sebagai plugin.

Juozas Kontvainis
sumber
Terima kasih banyak atas jawaban yang bagus ini ... Manfaat lain dari opsi kedua adalah ia juga bekerja dari metode utama, karena getClass () hanya berfungsi dari instance dan bukan dari metode statis.
Dan Ortega
6

Saya punya masalah ini sebelumnya dan saya membuat jalan mundur untuk memuat. Pada dasarnya cara pertama bekerja di dalam file .jar dan cara kedua berfungsi di dalam gerhana atau IDE lainnya.

public class MyClass {

    public static InputStream accessFile() {
        String resource = "my-file-located-in-resources.txt";

        // this is the path within the jar file
        InputStream input = MyClass.class.getResourceAsStream("/resources/" + resource);
        if (input == null) {
            // this is how we load file within editor (eg eclipse)
            input = MyClass.class.getClassLoader().getResourceAsStream(resource);
        }

        return input;
    }
}
MForm
sumber
3

Sampai sekarang (Desember 2017), ini adalah satu-satunya solusi yang saya temukan yang berfungsi baik di dalam maupun di luar IDE.

Gunakan PathMatchingResourcePatternResolver

Catatan: ini juga berfungsi di spring-boot

Dalam contoh ini saya membaca beberapa file yang terletak di src / main / resources / my_folder :

try {
    // Get all the files under this inner resource folder: my_folder
    String scannedPackage = "my_folder/*";
    PathMatchingResourcePatternResolver scanner = new PathMatchingResourcePatternResolver();
    Resource[] resources = scanner.getResources(scannedPackage);

    if (resources == null || resources.length == 0)
        log.warn("Warning: could not find any resources in this scanned package: " + scannedPackage);
    else {
        for (Resource resource : resources) {
            log.info(resource.getFilename());
            // Read the file content (I used BufferedReader, but there are other solutions for that):
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(resource.getInputStream()));
            String line = null;
            while ((line = bufferedReader.readLine()) != null) {
                // ...
                // ...                      
            }
            bufferedReader.close();
        }
    }
} catch (Exception e) {
    throw new Exception("Failed to read the resources folder: " + e.getMessage(), e);
}
Naor Bar
sumber
3

Masalahnya adalah bahwa perpustakaan pihak ketiga tertentu memerlukan nama path file daripada input stream. Sebagian besar jawaban tidak membahas masalah ini.

Dalam hal ini, satu solusi adalah menyalin konten sumber daya ke file sementara. Contoh berikut menggunakan jUnit TemporaryFolder.

    private List<String> decomposePath(String path){
        List<String> reversed = Lists.newArrayList();
        File currFile = new File(path);
        while(currFile != null){
            reversed.add(currFile.getName());
            currFile = currFile.getParentFile();
        }
        return Lists.reverse(reversed);
    }

    private String writeResourceToFile(String resourceName) throws IOException {
        ClassLoader loader = getClass().getClassLoader();
        InputStream configStream = loader.getResourceAsStream(resourceName);
        List<String> pathComponents = decomposePath(resourceName);
        folder.newFolder(pathComponents.subList(0, pathComponents.size() - 1).toArray(new String[0]));
        File tmpFile = folder.newFile(resourceName);
        Files.copy(configStream, tmpFile.toPath(), REPLACE_EXISTING);
        return tmpFile.getAbsolutePath();
    }
Navneeth
sumber
Ini banyak sekali. Terima kasih telah mendokumentasikannya. Saya ingin tahu opsi apa yang ada tanpa JUnit.
Alex Moore-Niemi
0

Jika Anda ingin membaca sebagai file, saya yakin masih ada solusi serupa:

    ClassLoader classLoader = getClass().getClassLoader();
    File file = new File(classLoader.getResource("file/test.xml").getFile());
pablo.vix
sumber
4
URL.getFile () tidak mengonversi URL ke nama file. Ini mengembalikan bagian dari URL setelah host, dengan semua persen-penyandian utuh, jadi jika path berisi karakter non-ASCII atau karakter ASCII yang tidak diizinkan dalam URL (termasuk spasi), hasilnya tidak akan menjadi nama file yang ada , meskipun URL adalah file:URL.
VGR
48
ini tidak berfungsi di dalam begitu program dibangun ke stoples
Akshay Kasar
1
Tidak berfungsi dari tabung kecuali Anda mengonversi ke string dan menyimpannya secara lokal terlebih dahulu.
smoosh911
0

Pastikan Anda bekerja dengan pemisah yang benar. Saya mengganti semua /di jalur relatif dengan File.separator. Ini bekerja dengan baik di IDE, namun tidak bekerja di build JAR.

Petterson
sumber
0

Saya pikir ini harusnya berfungsi di java juga. Kode berikut yang saya gunakan menggunakan kotlin.

val resource = Thread.currentThread().contextClassLoader.getResource('resources.txt')
irvifa
sumber
0

Saya telah menemukan perbaikan

BufferedReader br = new BufferedReader(new InputStreamReader(Main.class.getResourceAsStream(path)));

Ganti "Main" dengan kelas java tempat Anda memasukkannya. Ganti "path" dengan path di dalam file jar.

misalnya, jika Anda memasukkan State1.txt dalam paket com.issac.state, lalu ketikkan lintasan sebagai "/ com / issac / state / State1" jika Anda menjalankan Linux atau Mac. Jika Anda menjalankan Windows maka ketikkan path sebagai "\ com \ issac \ state \ State1". Jangan tambahkan ekstensi .txt ke file kecuali pengecualian File tidak ditemukan.

batal
sumber
-1

Anda dapat menggunakan pemuat kelas yang akan membaca dari classpath sebagai jalur ROOT (tanpa "/" di awal)

InputStream in = getClass().getClassLoader().getResourceAsStream("file.txt"); 
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
sendon1982
sumber
-1

Jika Anda menggunakan pegas, maka Anda dapat menggunakan metode berikut untuk membaca file dari src / main / resources:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.springframework.core.io.ClassPathResource;

  public String readFileToString(String path) throws IOException {

    StringBuilder resultBuilder = new StringBuilder("");
    ClassPathResource resource = new ClassPathResource(path);

    try (
        InputStream inputStream = resource.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {

      String line;

      while ((line = bufferedReader.readLine()) != null) {
        resultBuilder.append(line);
      }

    }

    return resultBuilder.toString();
  }
Sujan M.
sumber
1
Selamat datang di SO. Ini tidak memberikan jawaban untuk pertanyaan itu. Setelah memiliki reputasi yang cukup, Anda dapat mengomentari setiap pos. Periksa juga apa yang bisa saya lakukan .
Thewaywewere
Ini akan menghapus jeda baris dalam file!
elad.chen
-1

Untuk beberapa alasan classLoader.getResource()selalu mengembalikan nol ketika saya menggunakan aplikasi web ke WildFly 14. mendapatkan classLoader dari getClass().getClassLoader()atau Thread.currentThread().getContextClassLoader()mengembalikan null.

getClass().getClassLoader() API API mengatakan,

"Mengembalikan pemuat kelas untuk kelas. Beberapa implementasi mungkin menggunakan null untuk mewakili pemuat kelas bootstrap. Metode ini akan mengembalikan nol dalam implementasi seperti itu jika kelas ini dimuat oleh pemuat kelas bootstrap."

mungkin jika Anda menggunakan WildFly dan aplikasi web Anda coba ini

request.getServletContext().getResource()mengembalikan url sumber daya. Di sini permintaan adalah objek dari ServletRequest.

Ram C
sumber
-2

Kode di bawah ini berfungsi dengan Spring boot (kotlin):

val authReader = InputStreamReader(javaClass.getResourceAsStream("/file1.json"))
Bikash Das
sumber