Lettura di un file di risorse da jar


242

Vorrei leggere una risorsa dal mio vaso in questo modo:

File file;
file = new File(getClass().getResource("/file.txt").toURI());
BufferredReader reader = new BufferedReader(new FileReader(file));

//Read the file

e funziona benissimo quando lo eseguo in Eclipse, ma se lo esporto in un barattolo lo eseguo c'è un IllegalArgumentException:

Exception in thread "Thread-2"
java.lang.IllegalArgumentException: URI is not hierarchical

e non so davvero perché, ma con alcuni test ho scoperto se cambio

file = new File(getClass().getResource("/file.txt").toURI());

per

file = new File(getClass().getResource("/folder/file.txt").toURI());

quindi funziona al contrario (funziona in vaso ma non in eclissi).

Sto usando Eclipse e la cartella con il mio file è in una cartella di classe.


Se vuoi leggere i file da una directory in jar con qualsiasi numero per i file, vedi Stackoverflow-Link
Michael Hegner,

1
Non sono sicuro che la domanda originale riguardasse la primavera. Il collegamento nel commento precedente fa riferimento a una risposta specifica di primavera da una domanda diversa. Credo che getResourceAsStreamsia ancora una soluzione più semplice e portatile al problema.
Ha disegnato MacInnis il

Risposte:


398

Invece di provare a indirizzare la risorsa come file, basta chiedere a ClassLoader di restituire un InputStream per la risorsa invece tramite getResourceAsStream :

InputStream in = getClass().getResourceAsStream("/file.txt"); 
BufferedReader reader = new BufferedReader(new InputStreamReader(in));

Finché la file.txtrisorsa è disponibile sul percorso di classe, questo approccio funzionerà allo stesso modo indipendentemente dal fatto che la file.txtrisorsa si trovi in ​​una classes/directory o all'interno di una jar.

La URI is not hierarchicalverifica perché l'URI di una risorsa all'interno di un file jar è andare a guardare qualcosa di simile a questo: file:/example.jar!/file.txt. Non è possibile leggere le voci all'interno di un jar(un zipfile) come se fosse un semplice file vecchio .

Ciò è spiegato bene dalle risposte a:


3
Grazie, questo è stato molto utile e il codice funziona perfettamente, ma ho un problema, devo determinare se InputStreamesiste (come File.exists()) in modo che il mio gioco possa dire se usare il file predefinito o meno. Grazie.
PrinceCJC,

1
Oh e BTW il motivo per getClass().getResource("**/folder**/file.txt")cui ha funzionato è perché avevo quella cartella nella stessa directory del mio vaso :).
PrinceCJC,

5
getResourceAsStreamrestituisce null se la risorsa non esiste in modo che possa essere il test "esiste".
Ha disegnato MacInnis il

1
A proposito, hai un refuso: dovrebbe essere BufferedReader, non BufferredReader (nota la 'r' extra in seguito)
mailmindlin,

2
E ovviamente ... non dimenticare di chiudere inputStream e BufferedReader
Noremac,

26

Per accedere a un file in un barattolo hai due opzioni:

  • Posizionare il file nella struttura della directory corrispondente al nome del pacchetto (dopo aver estratto il file .jar, dovrebbe trovarsi nella stessa directory del file .class), quindi accedervi utilizzando getClass().getResourceAsStream("file.txt")

  • Posiziona il file nella radice (dopo aver estratto il file .jar, dovrebbe trovarsi nella radice), quindi accedilo utilizzando Thread.currentThread().getContextClassLoader().getResourceAsStream("file.txt")

La prima opzione potrebbe non funzionare quando jar viene utilizzato come plug-in.


Grazie mille per questa grande risposta ... Un altro vantaggio della seconda opzione è che funziona anche dal metodo principale, perché getClass () funziona solo da un'istanza e non da metodi statici.
Dan Ortega,

6

Ho avuto questo problema prima e ho effettuato il fallback per il caricamento. Fondamentalmente il primo modo funziona all'interno del file .jar e il secondo modo funziona all'interno di eclipse o altro IDE.

public class MyClass {

    public static InputStream accessFile() {
        String resource = "my-file-located-in-resources.txt";

        // this is the path within the jar file
        InputStream input = MyClass.class.getResourceAsStream("/resources/" + resource);
        if (input == null) {
            // this is how we load file within editor (eg eclipse)
            input = MyClass.class.getClassLoader().getResourceAsStream(resource);
        }

        return input;
    }
}

3

Fino ad ora (dicembre 2017), questa è l'unica soluzione che ho trovato che funziona sia all'interno che all'esterno dell'IDE.

Usa PathMatchingResourcePatternResolver

Nota: funziona anche con avvio a molla

In questo esempio sto leggendo alcuni file che si trovano in src / main / resources / my_folder :

try {
    // Get all the files under this inner resource folder: my_folder
    String scannedPackage = "my_folder/*";
    PathMatchingResourcePatternResolver scanner = new PathMatchingResourcePatternResolver();
    Resource[] resources = scanner.getResources(scannedPackage);

    if (resources == null || resources.length == 0)
        log.warn("Warning: could not find any resources in this scanned package: " + scannedPackage);
    else {
        for (Resource resource : resources) {
            log.info(resource.getFilename());
            // Read the file content (I used BufferedReader, but there are other solutions for that):
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(resource.getInputStream()));
            String line = null;
            while ((line = bufferedReader.readLine()) != null) {
                // ...
                // ...                      
            }
            bufferedReader.close();
        }
    }
} catch (Exception e) {
    throw new Exception("Failed to read the resources folder: " + e.getMessage(), e);
}

3

Il problema è che alcune librerie di terze parti richiedono nomi di file anziché flussi di input. La maggior parte delle risposte non risolve questo problema.

In questo caso, una soluzione alternativa consiste nel copiare il contenuto della risorsa in un file temporaneo. L'esempio seguente usa jUnit's TemporaryFolder.

    private List<String> decomposePath(String path){
        List<String> reversed = Lists.newArrayList();
        File currFile = new File(path);
        while(currFile != null){
            reversed.add(currFile.getName());
            currFile = currFile.getParentFile();
        }
        return Lists.reverse(reversed);
    }

    private String writeResourceToFile(String resourceName) throws IOException {
        ClassLoader loader = getClass().getClassLoader();
        InputStream configStream = loader.getResourceAsStream(resourceName);
        List<String> pathComponents = decomposePath(resourceName);
        folder.newFolder(pathComponents.subList(0, pathComponents.size() - 1).toArray(new String[0]));
        File tmpFile = folder.newFile(resourceName);
        Files.copy(configStream, tmpFile.toPath(), REPLACE_EXISTING);
        return tmpFile.getAbsolutePath();
    }

Questo è molto lucido. Grazie per averlo documentato. Sono curioso di sapere quali altre opzioni esistono senza JUnit.
Alex Moore-Niemi,

0

Se vuoi leggere come file, credo che ci sia ancora una soluzione simile:

    ClassLoader classLoader = getClass().getClassLoader();
    File file = new File(classLoader.getResource("file/test.xml").getFile());

4
URL.getFile () non converte un URL in un nome file. Restituisce la parte dell'URL dopo l'host, con tutte le codifiche percentuali intatte, quindi se il percorso contiene caratteri non ASCII o caratteri ASCII non consentiti negli URL (spazi inclusi), il risultato non sarà un nome file esistente , anche se l'URL è un file:URL.
VGR

48
questo non funziona all'interno una volta che il programma è stato creato su un vaso
Akshay Kasar,

1
Non funziona dal vaso a meno che non si converta in stringa e lo si salva prima in locale.
smoosh911,

0

Assicurati di lavorare con il separatore corretto. Ho sostituito tutto /in un percorso relativo con a File.separator. Funzionava bene nell'IDE, tuttavia non funzionava nel JAR di build.


0

Penso che questo dovrebbe funzionare anche in Java. Il seguente codice che uso sta usando kotlin.

val resource = Thread.currentThread().contextClassLoader.getResource('resources.txt')

0

Ho trovato una soluzione

BufferedReader br = new BufferedReader(new InputStreamReader(Main.class.getResourceAsStream(path)));

Sostituisci "Main" con la classe java in cui lo hai codificato. Sostituisci "path" con il percorso all'interno del file jar.

ad esempio, se si inserisce State1.txt nel pacchetto com.issac.state, digitare il percorso come "/ com / issac / state / State1" se si esegue Linux o Mac. Se esegui Windows, digita il percorso come "\ com \ issac \ state \ State1". Non aggiungere l'estensione .txt al file a meno che non si verifichi l'eccezione File non trovato.


-1

È possibile utilizzare il programma di caricamento classi che leggerà dal percorso di classe come percorso ROOT (senza "/" all'inizio)

InputStream in = getClass().getClassLoader().getResourceAsStream("file.txt"); 
BufferedReader reader = new BufferedReader(new InputStreamReader(in));

-1

Se stai usando spring, puoi usare il seguente metodo per leggere il file da src / main / resources:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.springframework.core.io.ClassPathResource;

  public String readFileToString(String path) throws IOException {

    StringBuilder resultBuilder = new StringBuilder("");
    ClassPathResource resource = new ClassPathResource(path);

    try (
        InputStream inputStream = resource.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {

      String line;

      while ((line = bufferedReader.readLine()) != null) {
        resultBuilder.append(line);
      }

    }

    return resultBuilder.toString();
  }

1
Benvenuti in SO. Questo non fornisce una risposta alla domanda. Una volta che avrai una reputazione sufficiente , sarai in grado di farlo commentare qualsiasi post. Controlla anche questo cosa posso fare invece .
thewaywewere

Questo rimuoverà le interruzioni di riga nel file!
elad.chen,

-1

Per qualche motivo, classLoader.getResource()quando ho distribuito l'applicazione Web su WildFly 14. ha sempre restituito un valore nullo, ottenendo classLoader getClass().getClassLoader()o restituendo Thread.currentThread().getContextClassLoader()null.

getClass().getClassLoader() Il documento API dice:

"Restituisce il caricatore di classi per la classe. Alcune implementazioni potrebbero utilizzare null per rappresentare il caricatore di classi bootstrap. Questo metodo restituirà null in tali implementazioni se questa classe è stata caricata dal caricatore di classi bootstrap."

potrebbe essere se stai usando WildFly e la tua applicazione web prova questo

request.getServletContext().getResource()ha restituito l'URL della risorsa. Qui la richiesta è un oggetto di ServletRequest.


-2

Il codice seguente funziona con Spring boot (kotlin):

val authReader = InputStreamReader(javaClass.getResourceAsStream("/file1.json"))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.