Ressource Java sous forme de fichier

122

Existe-t-il un moyen en Java de construire une instance de fichier sur une ressource récupérée à partir d'un fichier jar via le chargeur de classe?

Mon application utilise certains fichiers du jar (par défaut) ou d'un répertoire de système de fichiers spécifié lors de l'exécution (entrée utilisateur). Je recherche un moyen cohérent de
a) charger ces fichiers en tant que flux
b) lister les fichiers dans le répertoire défini par l'utilisateur ou dans le répertoire dans le jar respectivement

Edit: Apparemment, l'approche idéale serait de rester à l'écart de java.io.File. Existe-t-il un moyen de charger un répertoire à partir du chemin de classe et de répertorier son contenu (fichiers / entités qu'il contient)?

Mantrum
la source

Réponses:

58

ClassLoader.getResourceAsStreamet Class.getResourceAsStreamsont certainement la voie à suivre pour charger les données de ressources. Cependant, je ne crois pas qu'il existe un moyen de "lister" le contenu d'un élément du classpath.

Dans certains cas, cela peut être tout simplement impossible - par exemple, a ClassLoader pourrait générer des données à la volée, en fonction du nom de ressource demandé. Si vous regardez l' ClassLoaderAPI (qui est essentiellement ce que fonctionne le mécanisme de chemin de classe), vous verrez qu'il n'y a rien pour faire ce que vous voulez.

Si vous savez que vous avez réellement un fichier jar, vous pouvez le charger avec ZipInputStream pour savoir ce qui est disponible. Cela signifie que vous aurez un code différent pour les répertoires et les fichiers jar.

Une alternative, si les fichiers sont d'abord créés séparément, consiste à inclure une sorte de fichier manifeste contenant la liste des ressources disponibles. Regroupez-le dans le fichier jar ou incluez-le dans le système de fichiers en tant que fichier et chargez-le avant d'offrir à l'utilisateur un choix de ressources.

Jon Skeet
la source
3
Je suis d'accord que c'est ennuyeux - mais cela rend ClassLoader plus largement applicable par d'autres moyens. Par exemple, il est facile d'écrire un "chargeur de classe Web" parce que le Web est bon pour récupérer des fichiers, mais il ne répertorie généralement pas les fichiers.
Jon Skeet
Il semble que si la ressource que vous essayez de charger est beaucoup plus grande que 1 Mo, InputStreamvous obtenez des getResourceAsStreamarrêts pour récupérer les octets de la ressource après cette taille et retourne à la place 0 ssi elle est contenue dans un système de fichiers compressé comme un fichier jar. Vous semblez être obligé d'utiliser getResourceet de charger le fichier indépendamment de cela dans ce cas.
mgttlinger
1
@mgttlinger: Cela me semble peu probable ... ça vaut probablement la peine de poster une nouvelle question avec une repro si vous le pouvez.
Jon Skeet du
Le problème était dû à la readméthode déterminant la quantité de données à lire (je n'étais pas au courant de cela). Et il semble que tout le fichier soit lu si le fichier en cours de lecture se trouve dans un dossier normal. Si le fichier se trouve dans un bocal parce qu'il a été empaqueté, il n'en lit que des parties.
mgttlinger
1
@mgttlinger: Non, il ne lira pas que des parties de celui-ci - mais il pourrait ne pas remplir tout le tampon fourni à chaque appel de lecture. Comme toujours avec InputStream, si vous voulez lire toute la ressource, vous devez boucler jusqu'à ce que readrenvoie -1.
Jon Skeet
98

J'ai eu le même problème et j'ai pu utiliser ce qui suit:

// 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
la source
45
Cette approche ne fonctionne pas avec les JAR dans le
chemin de classe
1
@ yegor256 Si votre fichier est dans un bocal, vous pouvez créer un FileSystemobjet et en obtenir le Path. Ensuite, vous pouvez le lire comme d'habitude. (voir stackoverflow.com/a/22605905/1876344 )
mgttlinger
53

Voici un peu de code d'une de mes applications ... Faites-moi savoir si cela répond à vos besoins. Vous pouvez l'utiliser si vous connaissez le fichier que vous souhaitez utiliser.

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

J'espère que cela pourra aider.

Maurice Rogers
la source
Oui, mais toURI()échouera.
Olivier Faucheux
10

Un moyen fiable de construire une instance de fichier sur une ressource récupérée à partir d'un fichier jar est de copier la ressource en tant que flux dans un fichier temporaire (le fichier temporaire sera supprimé lorsque la JVM se fermera):

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
la source
4

Essaye ça:

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

Il existe d'autres méthodes disponibles, par exemple, voir ici: http://www.javaworld.com/javaworld/javaqa/2003-08/01-qa-0808-property.html

excellent chef
la source
Merci pour votre réponse. Je connais getResourceAsStream, mais je ne pense pas que je puisse charger un répertoire à partir du chemin de classe à travers cela.
Mantrum
Dans le répertoire Java se trouve un fichier (racines UNIX je suppose) donc vous devriez au moins essayer.
topchef
Je pense que pour lister les fichiers dans un répertoire, vous devrez utiliser java.io.File. Vous pouvez rechercher des fichiers avec le ClassLoader.findResource qui renvoie une URL qui peut être transmise à File.
Nathan Voxland
2

C'est une option: http://www.uofr.net/~greg/java/get-resource-listing.html

Poisson
la source
4
Bien que ce lien puisse répondre à la question, il serait préférable d'inclure les parties essentielles de la réponse ici et de fournir le lien pour référence, car un lien peut changer ou la page liée peut être supprimée, rendant ainsi la réponse inutile.
JonasCz - Réintégrer Monica le