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)?

Mantrum
sumber

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.

Jon Skeet
sumber
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()
Chris Conway
sumber
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.

Maurice Rogers
sumber
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;
    }
}
Lukas Masuch
sumber
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

topchef
sumber
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
2

Ini adalah salah satu opsi: http://www.uofr.net/~greg/java/get-resource-listing.html

Ikan
sumber
4
Meskipun tautan ini mungkin menjawab pertanyaan, akan lebih baik jika menyertakan bagian penting dari jawaban di sini, dan menyediakan tautan untuk referensi, karena tautan dapat berubah atau halaman yang ditautkan dapat dihapus, sehingga membuat jawaban tidak berguna.
JonasCz