it-swarm.com.de

Eine Ressourcendatei aus jar heraus lesen

Ich möchte eine Ressource in meinem Glas folgendermaßen 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 mit ein paar Tests habe ich herausgefunden, 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 im gegenteil (es funktioniert in jar aber nicht in eclipse).

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

197
PrinceCJC

Anstatt zu versuchen, die Ressource als Datei anzusprechen , fragen Sie einfach den ClassLoader Um stattdessen einen InputStream für die Ressource über getResourceAsStream zurückzugeben:

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

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

Der URI is not hierarchical tritt auf, weil der URI für eine Ressource in einer JAR-Datei ungefähr so ​​aussieht: file:/example.jar!/file.txt. Sie können die Einträge in einer jar (einer Zip -Datei) nicht lesen, als wäre es eine einfache alte Datei .

Dies wird durch die Antworten auf folgende Fragen gut erklärt:

332
Drew MacInnis

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

  • Platzieren Sie die Datei in der 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 getClass().getResourceAsStream("file.txt") darauf zu.

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

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

17

Wenn Sie als Datei lesen möchten, gibt es meines Erachtens noch eine ähnliche Lösung:

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

Ich hatte dieses Problem schon einmal und machte einen Fallback-Weg zum Laden. 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;
    }
}
3
MForm

Stellen Sie sicher, dass Sie mit dem richtigen Trennzeichen arbeiten. Ich habe alle / in einem relativen Pfad durch File.separator ersetzt. In der IDE hat dies problemlos funktioniert, in der Build-JAR jedoch nicht.

1
Petterson

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

Verwenden Sie PathMatchingResourcePatternResolver

Anmerkung: 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);
}
1
Naor Bar

Nach langem Herumstöbern in Java scheint die einzige Lösung für mich darin zu bestehen, die JAR-Datei selbst manuell zu lesen, es sei denn, Sie befinden sich in einer Entwicklungsumgebung (IDE):

/** @return The root folder or jar file that the class loader loaded from */
public static final File getClasspathFile() {
    return new File(YourMainClass.class.getProtectionDomain().getCodeSource().getLocation().getFile());
}

/** @param resource The path to the resource
 * @return An InputStream containing the resource's contents, or
 *         <b><code>null</code></b> if the resource does not exist */
public static final InputStream getResourceAsStream(String resource) {
    resource = resource.startsWith("/") ? resource : "/" + resource;
    if(getClasspathFile().isDirectory()) {//Development environment:
        return YourMainClass.class.getResourceAsStream(resource);
    }
    final String res = resource;//Jar or exe:
    return AccessController.doPrivileged(new PrivilegedAction<InputStream>() {
        @SuppressWarnings("resource")
        @Override
        public InputStream run() {
            try {
                final JarFile jar = new JarFile(getClasspathFile());
                String resource = res.startsWith("/") ? res.substring(1) : res;
                if(resource.endsWith("/")) {//Directory; list direct contents:(Mimics normal getResourceAsStream("someFolder/") behaviour)
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    Enumeration<JarEntry> entries = jar.entries();
                    while(entries.hasMoreElements()) {
                        JarEntry entry = entries.nextElement();
                        if(entry.getName().startsWith(resource) && entry.getName().length() > resource.length()) {
                            String name = entry.getName().substring(resource.length());
                            if(name.contains("/") ? (name.endsWith("/") && (name.indexOf("/") == name.lastIndexOf("/"))) : true) {//If it's a folder, we don't want the children's folders, only the parent folder's children!
                                name = name.endsWith("/") ? name.substring(0, name.length() - 1) : name;
                                baos.write(name.getBytes(StandardCharsets.UTF_8));
                                baos.write('\r');
                                baos.write('\n');
                            }
                        }
                    }
                    jar.close();
                    return new ByteArrayInputStream(baos.toByteArray());
                }
                JarEntry entry = jar.getJarEntry(resource);
                InputStream in = entry != null ? jar.getInputStream(entry) : null;
                if(in == null) {
                    jar.close();
                    return in;
                }
                final InputStream stream = in;//Don't manage 'jar' with try-with-resources or close jar until the
                return new InputStream() {//returned stream is closed(closing the jar closes all associated InputStreams):
                    @Override
                    public int read() throws IOException {
                        return stream.read();
                    }

                    @Override
                    public int read(byte b[]) throws IOException {
                        return stream.read(b);
                    }

                    @Override
                    public int read(byte b[], int off, int len) throws IOException {
                        return stream.read(b, off, len);
                    }

                    @Override
                    public long skip(long n) throws IOException {
                        return stream.skip(n);
                    }

                    @Override
                    public int available() throws IOException {
                        return stream.available();
                    }

                    @Override
                    public void close() throws IOException {
                        try {
                            jar.close();
                        } catch(IOException ignored) {
                        }
                        stream.close();
                    }

                    @Override
                    public synchronized void mark(int readlimit) {
                        stream.mark(readlimit);
                    }

                    @Override
                    public synchronized void reset() throws IOException {
                        stream.reset();
                    }

                    @Override
                    public boolean markSupported() {
                        return stream.markSupported();
                    }
                };
            } catch(Throwable e) {
                e.printStackTrace();
                return null;
            }
        }
    });
}

Hinweis: Der obige Code scheint nur für JAR-Dateien korrekt zu funktionieren, wenn er zur Hauptklasse gehört. Ich bin mir nicht sicher warum.

0
Brian_Entei

Der folgende Code funktioniert mit Spring Boot (Kotlin):

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

Wenn Sie spring verwenden, können Sie die Datei mit der folgenden Methode aus src/main/resources 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();
  }
0
Sujan M.

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

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

Sie können Klassenladeprogramm verwenden, das vom Klassenpfad als ROOT-Pfad liest (ohne "/" am Anfang).

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

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

In diesem Fall besteht eine Problemumgehung darin, den Ressourceninhalt in eine temporäre Datei zu kopieren. Im folgenden Beispiel wird jUnits TemporaryFolder verwendet.

    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();
    }
0
Navneeth

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

getClass().getClassLoader() API doc sagt:

"Gibt das Klassenladeprogramm für die Klasse zurück. Einige Implementierungen verwenden möglicherweise null, um das Klassenladeprogramm bootstrap darzustellen. Diese Methode gibt in solchen Implementierungen null zurück, wenn diese Klasse von der Klasse bootstrap geladen wurde Lader. "

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

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

0
Ram C