Sumber daya Java sebagai file


122

Apakah ada cara di Java untuk membuat instance File pada resource yang diambil dari jar melalui classloader?

Aplikasi saya menggunakan beberapa file dari jar (default) atau dari direktori sistem file yang ditentukan saat runtime (input pengguna). Saya mencari cara yang konsisten untuk
a) memuat file ini sebagai aliran
b) mencantumkan file di direktori yang ditentukan pengguna atau direktori di masing-masing jar

Sunting: Rupanya, pendekatan yang ideal akan menjauh dari java.io.File sama sekali. Apakah ada cara untuk memuat direktori dari classpath dan mencantumkan isinya (file / entitas yang ada di dalamnya)?

Jawaban:


58

ClassLoader.getResourceAsStreamdan Class.getResourceAsStreampastinya merupakan cara untuk memuat data sumber daya. Namun, saya tidak percaya ada cara untuk "membuat daftar" konten dari elemen classpath.

Dalam beberapa kasus, hal ini mungkin tidak mungkin - misalnya, a ClassLoader dapat menghasilkan data dengan cepat, berdasarkan nama resource apa yang diminta. Jika Anda melihat ClassLoaderAPI (yang pada dasarnya adalah mekanisme classpath bekerja) Anda akan melihat tidak ada yang bisa dilakukan sesuai keinginan Anda.

Jika Anda tahu Anda benar-benar memiliki file jar, Anda dapat memuatnya ZipInputStreamuntuk mencari tahu apa yang tersedia. Ini berarti Anda akan memiliki kode yang berbeda untuk direktori dan file jar.

Salah satu alternatif, jika file dibuat secara terpisah terlebih dahulu, adalah menyertakan semacam file manifes yang berisi daftar sumber daya yang tersedia. Gabungkan itu dalam file jar atau sertakan dalam sistem file sebagai file, dan muat sebelum menawarkan pilihan sumber daya kepada pengguna.


3
Saya setuju itu menjengkelkan - tetapi itu membuat ClassLoader lebih dapat diterapkan secara luas dengan cara lain. Misalnya, mudah untuk menulis "web classloader" karena web bagus untuk mengambil file, tetapi biasanya tidak mencantumkan file.
Jon Skeet

Sepertinya jika sumber daya yang Anda coba muat jauh lebih besar dari 1MiB, yang InputStreamAnda dapatkan dari getResourceAsStreamstop untuk mengambil byte sumber daya setelah ukuran itu dan sebagai gantinya mengembalikan 0 jika itu terdapat dalam sistem file terkompresi seperti jar. Anda tampaknya dipaksa untuk menggunakan getResourcedan memuat file secara terpisah dari ini dalam kasus itu.
mgttlinger

1
@mgttlinger: Kedengarannya tidak mungkin bagi saya ... mungkin layak memposting pertanyaan baru dengan repro jika Anda bisa.
Jon Skeet

Masalahnya adalah karena readmetode yang memutuskan berapa banyak data yang akan dibaca (saya tidak menyadarinya). Dan tampaknya seluruh file terbaca jika file yang sedang dibaca terletak di folder biasa. Jika file berada di dalam jar karena telah dikemas, file itu hanya membaca sebagian saja.
mgttlinger

1
@mgttlinger: Tidak, ini tidak hanya membaca sebagian - tapi mungkin tidak mengisi seluruh buffer yang disediakan pada setiap panggilan baca. Seperti biasa InputStream, jika Anda ingin membaca seluruh resource, Anda harus melakukan loop hingga readmengembalikan -1.
Jon Skeet

98

Saya memiliki masalah yang sama dan dapat menggunakan yang berikut ini:

// Load the directory as a resource
URL dir_url = ClassLoader.getSystemResource(dir_path);
// Turn the resource into a File object
File dir = new File(dir_url.toURI());
// List the directory
String files = dir.list()

45
Pendekatan ini tidak bekerja dengan JAR di classpath, hanya dengan file dan direktori
yegor256

1
@ yegor256 Jika file Anda ada di dalam jar, Anda dapat membuat FileSystemobjek dan mendapatkan Pathdari itu. Kemudian Anda bisa membacanya seperti biasa. (lihat stackoverflow.com/a/22605905/1876344 )
mgttlinger

53

Ini sedikit kode dari salah satu aplikasi saya ... Beri tahu saya jika sesuai dengan kebutuhan Anda. Anda dapat menggunakan ini jika Anda mengetahui file yang ingin Anda gunakan.

URL defaultImage = ClassA.class.getResource("/packageA/subPackage/image-name.png");
File imageFile = new File(defaultImage.toURI());

Semoga membantu.


Ya, ist tidak, tetapi toURI()akan gagal.
Olivier Faucheux

10

Cara yang dapat diandalkan untuk membuat instance File pada sumber daya yang diambil dari jar adalah dengan menyalin sumber daya sebagai aliran ke dalam File sementara (file temp akan dihapus ketika JVM keluar):

public static File getResourceAsFile(String resourcePath) {
    try {
        InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(resourcePath);
        if (in == null) {
            return null;
        }

        File tempFile = File.createTempFile(String.valueOf(in.hashCode()), ".tmp");
        tempFile.deleteOnExit();

        try (FileOutputStream out = new FileOutputStream(tempFile)) {
            //copy stream
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
        }
        return tempFile;
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}

4

Coba ini:

ClassLoader.getResourceAsStream ("some/pkg/resource.properties");

Ada lebih banyak metode yang tersedia, misalnya lihat di sini: http://www.javaworld.com/javaworld/javaqa/2003-08/01-qa-0808-property.html


Terimakasih atas tanggapan Anda. Saya tahu tentang getResourceAsStream, tetapi saya rasa saya tidak dapat memuat direktori dari classpath melalui itu.
Mantrum

Dalam direktori Java ada file (saya kira akar UNIX) jadi Anda setidaknya harus mencoba.
topchef

Saya pikir untuk membuat daftar file dalam direktori Anda akan perlu menggunakan java.io.File. Anda dapat mencari file dengan ClassLoader.findResource yang mengembalikan URL yang dapat diteruskan ke File.
Nathan Voxland

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.