web-dev-qa-db-fra.com

Comment lire un fichier de jar en Java?

Je souhaite lire un fichier XML situé dans l'un des jars inclus dans mon chemin d'accès aux classes. Comment puis-je lire n'importe quel fichier inclus dans jar?

55
kandarp

Si vous voulez lire ce fichier depuis votre application, utilisez:

InputStream input = getClass().getResourceAsStream("/classpath/to/my/file");

Le chemin commence par "/", mais ce n'est pas le chemin dans votre système de fichiers, mais dans votre chemin de classe. Ainsi, si votre fichier se trouve dans le chemin de classe "org.xml" et s'appelle myxml.xml, votre chemin ressemble à "/org/xml/myxml.xml".

InputStream lit le contenu de votre fichier. Vous pouvez l'envelopper dans un lecteur, si vous le souhaitez.

J'espère que ça aide.

105
Mnementh

Ah, c'est l'un de mes sujets préférés. Il existe essentiellement deux façons de charger une ressource via le classpath:

Class.getResourceAsStream(resource)

et

ClassLoader.getResourceAsStream(resource)

(Il existe d'autres méthodes qui impliquent d'obtenir une URL pour la ressource de la même manière, puis d'ouvrir une connexion à celle-ci, mais ce sont les deux méthodes directes).

La première méthode délègue en fait à la seconde, après avoir modifié le nom de la ressource. Il existe essentiellement deux types de noms de ressources: absolu (par exemple, "/ chemin/vers/ressource/ressource") et relatif (par exemple, "ressource"). Les chemins absolus commencent par "/".

Voici un exemple qui devrait illustrer. Considérons une classe com.example.A. Considérez deux ressources, l'une dans/com/exemple/nested, l'autre dans/top, dans le chemin de classe. Le programme suivant présente neuf manières d'accéder aux deux ressources:

 paquetage avec exemple; 
 
 public class A {
 
 public static void main (String args []) {
 
 // Class.getResourceAsStream 
 Ressource de l'objet = A.class.getResourceAsStream ("nested"); 
 System.out.println ("1: A.class nested =" + ressource); 
 
 ressource = A.class.getResourceAsStream ("/ com/exemple/nested"); 
 System.out.println ("2: A.class/com/example/nested = "+ resource); 
 
 resource = A.class.getResourceAsStream (" top "); 
 System.out.println (" 3: A.class top = "+ resource); 
 
 resource = A.class.getResourceAsStream ("/top "); 
 System.out.println (" 4: A.class/top = "+ ressource); 
 
 // ClassLoader.getResourceAsStream 
 ClassLoader cl = A.class.getClassLoader (); 
 resource = cl.getResourceAsStream (" nested "); 
 System.out.println ("5: cl nested =" + resource); 
 
 Resource = cl.getResourceAsStream ("/ com/exemple/nested"); 
 System.out.println ("6: cl/com/exemple/nested =" + ressource); 
 Resource = cl.getResourceAsStream ("com/exemple/nested"); 
 System.out.println ("7: clcom/exemple/nested =" + ressource); 
 
 Resource = cl.getResourceAsStream ("top"); 
 System.out .println ("8: cl top =" + ressource); 
 
 resource = cl.getResourceAsStream ("/ top"); 
 System.out.println ("9: cl/top = "+ ressource); 
} 
 
} 

Le résultat du programme est:

 1: A.class nested=Java.io.BufferedInputStream@19821f 
 2: A.class /com/example/nested=Java.io.BufferedInputStream@addbf1[.____. Grenade3: A.class top = null 
 4: A.class /top=Java.io.BufferedInputStream@42e816
5: cl nested = null 
 6: cl/com/example/nested = null 
 7: cl com/example/nested=Java.io.BufferedInputStream@9304b1 
 8: cl top=Java.io.BufferedInputStream@190d11 
 9: cl/top = null 

Généralement, les choses font ce que vous attendez. La casse-3 échoue car la résolution relative de la classe est relative à la classe. "Top" signifie donc "/ com/exemple/top", mais "/ top" signifie ce qu'il est écrit.

Case-5 échoue parce que la résolution relative du chargeur de classe concerne le chargeur de classe. Mais, de manière inattendue, l'affaire 6 échoue également: on pourrait s'attendre à ce que "/ com/exemple/nested" soit résolu correctement. Pour accéder à une ressource imbriquée via le chargeur de classe, vous devez utiliser Case-7, c’est-à-dire que le chemin imbriqué est relatif à la racine du chargeur de classe. De même, le cas 9 échoue, mais le cas 8 passe.

Rappelez-vous: pour Java.lang.Class, getResourceAsStream () délègue au chargeur de classe:

 public InputStream getResourceAsStream (Nom de la chaîne) {
 name = resolName (name); 
 ClassLoader cl = getClassLoader0 (); 
 if (cl == null) { 
 // Une classe système. 
 Renvoyer ClassLoader.getSystemResourceAsStream (nom); 
} 
 Renvoyer cl.getResourceAsStream (nom); 
} 

c'est donc le comportement de resolName () qui est important.

Enfin, puisque c’est le comportement du chargeur de classe qui a chargé la classe qui contrôle essentiellement getResourceAsStream (), et que le chargeur de classe est souvent un chargeur personnalisé, les règles de chargement des ressources peuvent être encore plus complexes. par exemple. pour les applications Web, chargez depuis WEB-INF/classes ou WEB-INF/lib dans le contexte de l'application Web, mais pas depuis d'autres applications Web isolées. De plus, les chargeurs de classes bien élevés délèguent leurs responsabilités aux parents, de sorte que les ressources dupliquées dans le chemin d'accès aux classes risquent de ne pas être accessibles via ce mécanisme.

53
simontuffs

Vérifiez d'abord votre chargeur de cours.

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

if (classLoader == null) {
    classLoader = Class.class.getClassLoader();
}

classLoader.getResourceAsStream("xmlFileNameInJarFile.xml");

// xml file location at xxx.jar
// + folder
// + folder
// xmlFileNameInJarFile.xml
8
hothead

Un fichier JAR est fondamentalement un fichier Zip, alors traitez-le comme tel. Vous trouverez ci-dessous un exemple sur la façon d'extraire un fichier d'un fichier WAR (il s'agit également d'un fichier Zip) et d'afficher le contenu de la chaîne. Pour binaire, vous aurez besoin de modifier le processus d’extraction, mais il existe de nombreux exemples pour cela.

public static void main(String args[]) {
    String relativeFilePath = "style/someCSSFile.css";
    String zipFilePath = "/someDirectory/someWarFile.war";
    String contents = readZipFile(zipFilePath,relativeFilePath);
    System.out.println(contents);
}

public static String readZipFile(String zipFilePath, String relativeFilePath) {
    try {
        ZipFile zipFile = new ZipFile(zipFilePath);
        Enumeration<? extends ZipEntry> e = zipFile.entries();

        while (e.hasMoreElements()) {
            ZipEntry entry = (ZipEntry) e.nextElement();
            // if the entry is not directory and matches relative file then extract it
            if (!entry.isDirectory() && entry.getName().equals(relativeFilePath)) {
                BufferedInputStream bis = new BufferedInputStream(
                        zipFile.getInputStream(entry));
                // Read the file
                    // With Apache Commons I/O
                 String fileContentsStr = IOUtils.toString(bis, "UTF-8");

                    // With Guava
                //String fileContentsStr = new String(ByteStreams.toByteArray(bis),Charsets.UTF_8);
                // close the input stream.
                bis.close();
                return fileContentsStr;
            } else {
                continue;
            }
        }
    } catch (IOException e) {
        logger.error("IOError :" + e);
        e.printStackTrace();
    }
    return null;
}

Dans cet exemple, j'utilise Apache Commons I/O et si vous utilisez Maven, voici la dépendance:

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.4</version>
</dependency>
2
Tristan Everitt

Juste pour être complet, il y a eu récemment une question sur la liste de diffusion Jython où l'une des réponses faisait référence à ce fil.

La question était de savoir comment appeler un script Python contenu dans un fichier .jar à partir de Jython, la réponse suggérée est la suivante (avec "InputStream" comme expliqué dans l'une des réponses ci-dessus:

PythonInterpreter.execfile(InputStream)
0
he1ix