2010-07-30 25 views
51

Voglio leggere un file XML che si trova all'interno di uno degli jar s inclusi nel mio percorso di classe. Come posso leggere qualsiasi file incluso nello jar?Come leggere un file da jar in Java?

+0

vedere http://stackoverflow.com/questions/16842306 – yegor256

+0

Se volete leggere i file da una directory in vaso con qualsiasi numero di file, vedere [StackOverflow-link] (https://stackoverflow.com/questions/26185137/ primavera-boot-resource-non-trovato-quando-con-executeable-jar/39.818.817 # 39.818.817) –

risposta

102

Se si desidera leggere il file da dentro l'utilizzo dell'applicazione:

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

Il percorso inizia con "/", ma che non è il percorso nel file-system, ma nel classpath. Quindi, se il file si trova nel classpath "org.xml" e si chiama myxml.xml, il percorso sembra "/org/xml/myxml.xml".

InputStream legge il contenuto del file. Puoi avvolgerlo in un Reader, se vuoi.

Spero che questo aiuti.

+17

+1 per explaning perché il principale "/" è necessario. –

46

Ah, questa è una delle mie materie preferite. Ci sono essenzialmente due modi per caricare una risorsa attraverso il percorso di classe:

Class.getResourceAsStream(resource) 

e

ClassLoader.getResourceAsStream(resource) 

(ci sono altri modi che coinvolgono ottenere un URL per la risorsa in modo simile, quindi aprendo una connessione ad esso, ma questi sono i due modi diretti).

Il primo metodo in realtà delega al secondo, dopo aver distrutto il nome della risorsa. Esistono essenzialmente due tipi di nomi di risorse: assoluto (ad esempio "/ percorso/a/risorsa/risorsa") e relativo (ad esempio "risorsa"). I percorsi assoluti iniziano con "/".

Ecco un esempio che dovrebbe illustrare. Considera una classe com.esempio.A. Considerare due risorse, una situata in/com/example/nested, l'altra at/top, nel classpath. Il seguente programma mostra nove possibili modi per accedere ai due risorse:

 
package com.example; 

public class A { 

    public static void main(String args[]) { 

     // Class.getResourceAsStream 
     Object resource = A.class.getResourceAsStream("nested"); 
     System.out.println("1: A.class nested=" + resource); 

     resource = A.class.getResourceAsStream("/com/example/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=" + resource); 

     // ClassLoader.getResourceAsStream 
     ClassLoader cl = A.class.getClassLoader(); 
     resource = cl.getResourceAsStream("nested");   
     System.out.println("5: cl nested=" + resource); 

     resource = cl.getResourceAsStream("/com/example/nested"); 
     System.out.println("6: cl /com/example/nested=" + resource); 
     resource = cl.getResourceAsStream("com/example/nested"); 
     System.out.println("7: cl com/example/nested=" + resource); 

     resource = cl.getResourceAsStream("top"); 
     System.out.println("8: cl top=" + resource); 

     resource = cl.getResourceAsStream("/top"); 
     System.out.println("9: cl /top=" + resource); 
    } 

} 

L'output del programma è:

 
1: A.class [email protected] 
2: A.class /com/example/[email protected] 
3: A.class top=null 
4: A.class /[email protected] 
5: cl nested=null 
6: cl /com/example/nested=null 
7: cl com/example/[email protected] 
8: cl [email protected] 
9: cl /top=null 

Per lo più cose fare ciò che ci si aspetterebbe. Il caso 3 fallisce perché la risoluzione relativa della classe è rispetto alla classe, quindi "top" significa "/ com/example/top", ma "/ top" significa ciò che dice.

Il caso 5 non riesce perché la risoluzione relativa del classloader è rispetto al classloader. Ma, anche inaspettatamente, Case-6 fallisce: ci si potrebbe aspettare che "/ com/example/nested" si risolva correttamente. Per accedere a una risorsa nidificata tramite il classloader è necessario utilizzare Case-7, cioè il percorso nidificato è relativo alla radice del classloader. Allo stesso modo, Case-9 fallisce, ma passa Case-8.

Ricorda: per java.lang.Class, getResourceAsStream() non delegare al classloader:

 
    public InputStream getResourceAsStream(String name) { 
     name = resolveName(name); 
     ClassLoader cl = getClassLoader0(); 
     if (cl==null) { 
      // A system class. 
      return ClassLoader.getSystemResourceAsStream(name); 
     } 
     return cl.getResourceAsStream(name); 
    } 

quindi è il comportamento di ResolveName(), che è importante.

Infine, poiché è il comportamento del classloader che ha caricato la classe che controlla essenzialmente getResourceAsStream() e il classloader è spesso un caricatore personalizzato, le regole di caricamento delle risorse potrebbero essere ancora più complesse. per esempio.per applicazioni Web, caricare da WEB-INF/classes o WEB-INF/lib nel contesto dell'applicazione Web, ma non da altre applicazioni Web isolate. Inoltre, i classloader ben educati delegano ai genitori, in modo che le risorse duplicate nel classpath potrebbero non essere accessibili utilizzando questo meccanismo.

+0

Questo è essenzialmente la stessa risposta che ho dato, ma ha spiegato in modo molto maggiori dettagli. +1 – Mnementh

+0

Ecco il metodo resolveName menzionato: https://gist.github.com/briangordon/5374626 –

2

Un JAR è fondamentalmente un file ZIP quindi trattalo come tale. Di seguito è riportato un esempio su come estrarre un file da un file WAR (trattarlo anche come file ZIP) e restituire il contenuto della stringa. Per il binario dovrai modificare il processo di estrazione, ma ci sono un sacco di esempi là fuori per questo.

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; 
} 

In questo esempio sto usando Apache Commons I/O e se si utilizza Maven qui è la dipendenza:

<dependency> 
    <groupId>commons-io</groupId> 
    <artifactId>commons-io</artifactId> 
    <version>2.4</version> 
</dependency> 
8

controllare prima la classe Loader.

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 
0

Solo per completezza, non v'è stato recentemente un question sulla mailinglist Jython in cui una delle risposte di cui a questo thread.

La domanda era: come chiamare uno script Python che è contenuto in un file .jar dall'interno Jython, la risposta suggerita è la seguente (con "InputStream" come spiegato in una delle risposte di cui sopra:

PythonInterpreter.execfile(InputStream)