Lesen einer Ressourcendatei aus dem Glas

241

Ich möchte eine Ressource aus meinem Glas so lesen:

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

//Read the file

und es funktioniert gut, wenn es in Eclipse ausgeführt wird, aber wenn ich es in ein JAR exportiere, gibt es eine IllegalArgumentException:

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

und ich weiß wirklich nicht warum, aber bei einigen Tests habe ich festgestellt, ob ich mich ändere

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

zu

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

dann funktioniert es umgekehrt (es funktioniert im Glas, aber nicht in der Finsternis).

Ich verwende Eclipse und der Ordner mit meiner Datei befindet sich in einem Klassenordner.

PrinceCJC
quelle
Wenn Sie Dateien aus einem Verzeichnis in jar mit beliebigen Nummern für Dateien lesen möchten, siehe Stackoverflow-Link
Michael Hegner
1
Ich bin mir nicht sicher, ob die ursprüngliche Frage den Frühling betraf. Der Link im vorherigen Kommentar verweist auf eine Spring-spezifische Antwort auf eine andere Frage. Ich glaube, es getResourceAsStreamist immer noch eine einfachere und tragbarere Lösung für das Problem.
Drew MacInnis

Antworten:

396

Anstatt zu versuchen, die Ressource als Datei zu adressieren, bitten Sie den ClassLoader , stattdessen über getResourceAsStream einen InputStream für die Ressource zurückzugeben :

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

Solange die file.txtRessource im Klassenpfad verfügbar ist, funktioniert dieser Ansatz auf dieselbe Weise, unabhängig davon, ob sich die file.txtRessource in einem classes/Verzeichnis oder in einem befindet jar.

Das URI is not hierarchicaltritt auf, weil die URI für eine Ressource innerhalb einer JAR - Datei wird in etwa so aussehen werde: file:/example.jar!/file.txt. Sie können die Einträge in einer jar(einer zipDatei) nicht lesen, als wäre es eine einfache alte Datei .

Dies wird gut erklärt durch die Antworten auf:

Drew MacInnis
quelle
3
Vielen Dank, dies war sehr hilfreich und der Code funktioniert perfekt, aber ich habe ein Problem: Ich muss feststellen, ob das InputStreamvorhanden ist (wie File.exists()), damit mein Spiel erkennen kann, ob die Standarddatei verwendet werden soll oder nicht. Vielen Dank.
PrinceCJC
1
Oh und übrigens, der Grund getClass().getResource("**/folder**/file.txt"), warum es funktioniert hat, ist, dass ich diesen Ordner im selben Verzeichnis wie mein Glas hatte :).
PrinceCJC
5
getResourceAsStreamGibt null zurück, wenn die Ressource nicht vorhanden ist, sodass dies Ihr "vorhandener" Test sein kann.
Drew MacInnis
1
Übrigens, Sie haben einen Tippfehler: Es sollte BufferedReader sein, nicht BufferredReader (beachten Sie das zusätzliche 'r' im späteren)
mailmindlin
2
Und natürlich ... vergessen Sie nicht, den inputStream und BufferedReader
Noremac
26

Um auf eine Datei in einem Glas zuzugreifen, haben Sie zwei Möglichkeiten:

  • Platzieren Sie die Datei in einer Verzeichnisstruktur, die Ihrem Paketnamen entspricht (nach dem Extrahieren der JAR-Datei sollte sie sich im selben Verzeichnis wie die .class-Datei befinden), und greifen Sie dann mit darauf zu getClass().getResourceAsStream("file.txt")

  • Platzieren Sie die Datei im Stammverzeichnis (nach dem Extrahieren der JAR-Datei sollte sie sich im Stammverzeichnis befinden) und greifen Sie dann mit darauf zu Thread.currentThread().getContextClassLoader().getResourceAsStream("file.txt")

Die erste Option funktioniert möglicherweise nicht, wenn jar als Plugin verwendet wird.

Juozas Kontvainis
quelle
Vielen Dank für diese großartige Antwort ... Ein weiterer Vorteil der zweiten Option ist, dass sie auch mit der Hauptmethode funktioniert, da getClass () nur mit einer Instanz und nicht mit statischen Methoden funktioniert.
Dan Ortega
6

Ich hatte dieses Problem schon einmal und habe einen Fallback-Weg zum Laden gemacht. Grundsätzlich funktioniert der erste Weg innerhalb einer JAR-Datei und der zweite Weg innerhalb von Eclipse oder einer anderen 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;
    }
}
MForm
quelle
3

Bis jetzt (Dezember 2017) ist dies die einzige Lösung, die ich gefunden habe und die sowohl innerhalb als auch außerhalb der IDE funktioniert .

Verwenden Sie PathMatchingResourcePatternResolver

Hinweis: Es funktioniert auch im Spring-Boot

In diesem Beispiel lese ich einige Dateien 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);
}
Naor Bar
quelle
3

Das Problem ist, dass bestimmte Bibliotheken von Drittanbietern Dateipfadnamen anstelle von Eingabestreams benötigen. Die meisten Antworten sprechen dieses Problem nicht an.

In diesem Fall besteht eine Problemumgehung darin, den Ressourceninhalt in eine temporäre Datei zu kopieren. Im folgenden Beispiel werden jUnits verwendet 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();
    }
Navneeth
quelle
Dies wird viel beschönigt. Vielen Dank für die Dokumentation. Ich bin gespannt, welche anderen Optionen es ohne JUnit gibt.
Alex Moore-Niemi
0

Wenn Sie als Datei lesen möchten, gibt es meiner Meinung nach immer noch eine ähnliche Lösung:

    ClassLoader classLoader = getClass().getClassLoader();
    File file = new File(classLoader.getResource("file/test.xml").getFile());
pablo.vix
quelle
4
URL.getFile () nicht konvertiert eine URL zu einem Dateinamen. Es gibt den Teil der URL nach dem Host zurück, wobei alle Prozentkodierungen intakt sind. Wenn der Pfad also Nicht-ASCII-Zeichen oder ASCII-Zeichen enthält, die in URLs (einschließlich Leerzeichen) nicht zulässig sind, ist das Ergebnis kein vorhandener Dateiname , auch wenn die URL eine file:URL ist.
VGR
48
Dies funktioniert nicht im Inneren, sobald das Programm in einem Glas erstellt wurde
Akshay Kasar
1
Funktioniert nicht aus jar, es sei denn, Sie konvertieren in einen String und speichern ihn zuerst lokal.
Smoosh911
0

Stellen Sie sicher, dass Sie mit dem richtigen Trennzeichen arbeiten. Ich habe alle /in einem relativen Pfad durch a ersetzt File.separator. Dies funktionierte in der IDE einwandfrei, in der Build-JAR jedoch nicht.

Petterson
quelle
0

Ich denke, das sollte auch in Java funktionieren. Der folgende Code, den ich benutze, verwendet Kotlin.

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

Ich habe eine Lösung gefunden

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

Ersetzen Sie "Main" durch die Java-Klasse, in der Sie es codiert haben. Ersetzen Sie "path" durch den Pfad in der JAR-Datei.

Wenn Sie beispielsweise State1.txt in das Paket com.issac.state einfügen, geben Sie den Pfad als "/ com / issac / state / State1" ein, wenn Sie Linux oder Mac ausführen. Wenn Sie Windows ausführen, geben Sie den Pfad als "\ com \ issac \ state \ State1" ein. Fügen Sie der Datei die Erweiterung .txt nur hinzu, wenn die Ausnahme Datei nicht gefunden auftritt.

Null
quelle
-1

Sie können den Klassenlader verwenden, der aus dem Klassenpfad als ROOT-Pfad liest (ohne "/" am Anfang).

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

Wenn Sie spring verwenden, können Sie die folgende Methode verwenden, um Dateien aus src / main / resources zu lesen:

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();
  }
Sujan M.
quelle
1
Willkommen bei SO. Dies gibt keine Antwort auf die Frage. Sobald Sie einen ausreichenden Ruf haben, können Sie jeden Beitrag kommentieren . Überprüfen Sie auch, was ich stattdessen tun kann .
thewaywewere
Dadurch werden die Zeilenumbrüche in der Datei entfernt!
elad.chen
-1

Aus irgendeinem Grund wurde classLoader.getResource()immer null zurückgegeben, wenn ich die Webanwendung für WildFly 14 bereitgestellt habe. ClassLoader von null wird abgerufen getClass().getClassLoader()oder Thread.currentThread().getContextClassLoader()zurückgegeben.

getClass().getClassLoader() API-Dokument sagt,

"Gibt den Klassenlader für die Klasse zurück. Einige Implementierungen verwenden möglicherweise null, um den Bootstrap-Klassenlader darzustellen. Diese Methode gibt in solchen Implementierungen null zurück, wenn diese Klasse vom Bootstrap-Klassenlader geladen wurde."

Wenn Sie WildFly und Ihre Webanwendung verwenden, versuchen Sie dies möglicherweise

request.getServletContext().getResource()gab die Ressourcen-URL zurück. Hier ist request ein Objekt von ServletRequest.

Ram C.
quelle
-2

Der folgende Code funktioniert mit Spring Boot (Kotlin):

val authReader = InputStreamReader(javaClass.getResourceAsStream("/file1.json"))
Bikash Das
quelle