Frage

Ich habe diesen Code, der alle Dateien aus einem Verzeichnis liest.

    File textFolder = new File("text_directory");

    File [] texFiles = textFolder.listFiles( new FileFilter() {
           public boolean accept( File file ) {
               return file.getName().endsWith(".txt");
           }
    });

Es funktioniert großartig. Es füllt das Array mit allen Dateien, die mit „.txt“ enden aus dem Verzeichnis ‚text_directory‘.

Wie können wir lesen, den Inhalt eines Verzeichnisses in einer ähnlichen Art und Weise innerhalb eine JAR-Datei?

Also, was ich wirklich tun wollen ist, alle Bilder in meinem JAR-Datei zur Liste, so kann ich sie mit laden:

ImageIO.read(this.getClass().getResource("CompanyLogo.png"));

(Dass man arbeitet, weil die „Company“ ist „hartcodiert“, aber die Anzahl der Bilder in der JAR-Datei von 10 bis 200 mit variabler Länge sein könnte.)

Bearbeiten

Also ich denke, mein Hauptproblem sei: Wie der Name der JAR-Datei wissen, , wo mein Hauptklasse Leben

Zugegeben, ich könnte es lesen java.util.Zip verwenden.

Meine Struktur ist wie folgt:

Sie sind wie:

my.jar!/Main.class
my.jar!/Aux.class
my.jar!/Other.class
my.jar!/images/image01.png
my.jar!/images/image02a.png
my.jar!/images/imwge034.png
my.jar!/images/imagAe01q.png
my.jar!/META-INF/manifest 

Im Moment bin ich in der Lage zum Beispiel laden "images / image01.png" mit:

    ImageIO.read(this.getClass().getResource("images/image01.png));

Aber nur, weil ich den Namen der Datei kennen, für den Rest Ich habe sie dynamisch zu laden.

War es hilfreich?

Lösung

CodeSource src = MyClass.class.getProtectionDomain().getCodeSource();
if (src != null) {
  URL jar = src.getLocation();
  ZipInputStream zip = new ZipInputStream(jar.openStream());
  while(true) {
    ZipEntry e = zip.getNextEntry();
    if (e == null)
      break;
    String name = e.getName();
    if (name.startsWith("path/to/your/dir/")) {
      /* Do something with this entry. */
      ...
    }
  }
} 
else {
  /* Fail... */
}

Beachten Sie, dass in Java 7, können Sie eine FileSystem aus der JAR-Datei (zip) Datei erstellen und dann NIO das Verzeichnis zu Fuß und Filtermechanismen durch sie suchen. Dies würde es einfacher, Code zu schreiben, JAR-Dateien und „explodiert“ Verzeichnisse behandelt.

Andere Tipps

Code, der für beide IDE und .jar-Dateien funktioniert:

import java.io.*;
import java.net.*;
import java.nio.file.*;
import java.util.*;
import java.util.stream.*;

public class ResourceWalker {
    public static void main(String[] args) throws URISyntaxException, IOException {
        URI uri = ResourceWalker.class.getResource("/resources").toURI();
        Path myPath;
        if (uri.getScheme().equals("jar")) {
            FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap());
            myPath = fileSystem.getPath("/resources");
        } else {
            myPath = Paths.get(uri);
        }
        Stream<Path> walk = Files.walk(myPath, 1);
        for (Iterator<Path> it = walk.iterator(); it.hasNext();){
            System.out.println(it.next());
        }
    }
}

beantworten hat perfekt funktioniert:

Hier ist der Arbeitscode.

CodeSource src = MyClass.class.getProtectionDomain().getCodeSource();
List<String> list = new ArrayList<String>();

if( src != null ) {
    URL jar = src.getLocation();
    ZipInputStream zip = new ZipInputStream( jar.openStream());
    ZipEntry ze = null;

    while( ( ze = zip.getNextEntry() ) != null ) {
        String entryName = ze.getName();
        if( entryName.startsWith("images") &&  entryName.endsWith(".png") ) {
            list.add( entryName  );
        }
    }

 }
 webimages = list.toArray( new String[ list.size() ] );

Und ich habe ändern nur meine Load-Methode von diesem:

File[] webimages = ... 
BufferedImage image = ImageIO.read(this.getClass().getResource(webimages[nextIndex].getName() ));

Um dies zu:

String  [] webimages = ...

BufferedImage image = ImageIO.read(this.getClass().getResource(webimages[nextIndex]));

Ich möchte acheron55 der private static ConcurrentMap<String, Object> locks = new ConcurrentHashMap<>(); public void walk(String path) throws Exception { URI uri = getClass().getResource(path).toURI(); if ("jar".equals(uri.getScheme()) { safeWalkJar(path, uri); } else { Files.walk(Paths.get(path)); } } private void safeWalkJar(String path, URI uri) throws Exception { synchronized (getLock(uri)) { // this'll close the FileSystem object at the end try (FileSystem fs = getFileSystem(uri)) { Files.walk(fs.getPath(path)); } } } private Object getLock(URI uri) { String fileName = parseFileName(uri); locks.computeIfAbsent(fileName, s -> new Object()); return locks.get(fileName); } private String parseFileName(URI uri) { String schemeSpecificPart = uri.getSchemeSpecificPart(); return schemeSpecificPart.substring(0, schemeSpecificPart.indexOf("!")); } private FileSystem getFileSystem(URI uri) throws IOException { try { return FileSystems.getFileSystem(uri); } catch (FileSystemNotFoundException e) { return FileSystems.newFileSystem(uri, Collections.<String, String>emptyMap()); } }

Es gibt keinen wirklichen Bedarf über die Dateinamen zu synchronisieren; man könnte einfach auf demselben Objekt synchronisieren jedes Mal (oder die Methode synchronized machen), es ist rein eine Optimierung.

Ich würde sagen, dass dies immer noch eine problematische Lösung, da es andere Teile in dem Code, der die FileSystem Schnittstelle über die gleichen Dateien verwenden könnte, und es könnte mit ihnen (auch in einer Single-Threaded-Anwendung) stören.
Auch er sucht nicht für nulls (zum Beispiel auf getClass().getResource().

Diese besondere Java NIO-Schnittstelle ist eine Art schrecklich, da es eine global / Singletons nicht Thread-sichere Ressource führt, und deren Dokumentation ist sehr vage (viele Unbekannten aufgrund providerspezifische Implementierungen). Die Ergebnisse können für andere FileSystem Anbietern (nicht JAR) variieren. Vielleicht gibt es einen guten Grund dafür, dass Art und Weise zu sein; Ich weiß nicht, ich habe nicht die Implementierungen erforscht.

Hier ist eine Methode, die ich für ein „laufen alle JUnits unter einem Paket“ schrieb. Sie sollten es an Ihre Bedürfnisse anpassen können.

private static void findClassesInJar(List<String> classFiles, String path) throws IOException {
    final String[] parts = path.split("\\Q.jar\\\\E");
    if (parts.length == 2) {
        String jarFilename = parts[0] + ".jar";
        String relativePath = parts[1].replace(File.separatorChar, '/');
        JarFile jarFile = new JarFile(jarFilename);
        final Enumeration<JarEntry> entries = jarFile.entries();
        while (entries.hasMoreElements()) {
            final JarEntry entry = entries.nextElement();
            final String entryName = entry.getName();
            if (entryName.startsWith(relativePath)) {
                classFiles.add(entryName.replace('/', File.separatorChar));
            }
        }
    }
}

Edit: Ach, in diesem Fall könnten Sie diesen Schnipsel wollen auch (gleicher Anwendungsfall :))

private static File findClassesDir(Class<?> clazz) {
    try {
        String path = clazz.getProtectionDomain().getCodeSource().getLocation().getFile();
        final String codeSourcePath = URLDecoder.decode(path, "UTF-8");
        final String thisClassPath = new File(codeSourcePath, clazz.getPackage().getName().repalce('.', File.separatorChar));
    } catch (UnsupportedEncodingException e) {
        throw new AssertionError("impossible", e);
    }
}
  
    

Also ich denke, mein Hauptproblem wäre, wie der Name des Glases zu wissen, wo meine Hauptklasse lebt.

  

Unter der Annahme, dass Ihr Projekt in einem Glas verpackt ist (nicht unbedingt wahr!), Können Sie ClassLoader.getResource () oder Findresource () mit dem Klassennamen (gefolgt von .class) das Glas zu erhalten, die eine bestimmte Klasse enthält . Sie finden die jar Namen von der URL analysieren müssen, die (nicht so schwer) zurückgegeben werden, die ich als eine Übung für die Leser überlassen werden: -)

Achten Sie darauf, für den Fall zu prüfen, wo die Klasse nicht Teil eines Glases ist.

Eine JAR-Datei ist nur eine Zip-Datei mit einem strukturierten manifestieren. Sie können mit den üblichen Java-Zip-Tool, um die JAR-Datei öffnen und dem Inhalt der Datei auf diese Weise scannen, aufblasen Ströme usw. Dann, dass die Verwendung in einem getResourceAsStream Anruf, und es sollte alle beste Ordnung sein.

EDIT / nach Klärung

Es dauerte eine Minute zu erinnern, alle Bits und Stücke, und ich bin sicher, dass es saubere Möglichkeiten, es zu tun, aber ich wollte sehen, dass ich nicht verrückt war. In meinem Projekt image.jpg ist eine Datei in einem Teil der Haupt JAR-Datei. Ich erhalte die Klassenlader der Hauptklasse (Someclass ist der Einstiegspunkt) und es verwenden, die image.jpg Ressource zu entdecken. Dann Magie einige Bach es in diesem ImageInputStream Sache zu bekommen und alles ist in Ordnung.

InputStream inputStream = SomeClass.class.getClassLoader().getResourceAsStream("image.jpg");
JPEGImageReaderSpi imageReaderSpi = new JPEGImageReaderSpi();
ImageReader ir = imageReaderSpi.createReaderInstance();
ImageInputStream iis = new MemoryCacheImageInputStream(inputStream);
ir.setInput(iis);
....
ir.read(0); //will hand us a buffered image

Da eine tatsächliche JAR-Datei, können Sie den Inhalt mit JarFile.entries() auflisten. Sie müssen den Speicherort der JAR-Datei allerdings wissen, -. Sie können nicht nur den Klassenlader fragen alles aufzulisten es bekommen konnte

Sie sollten den Speicherort der JAR-Datei auf der von ThisClassName.class.getResource("ThisClassName.class") zurück URL basierend erarbeiten können, aber es kann ein kleines bisschen knifflig sein.

Vor einiger Zeit habe ich eine Funktion, die jeweilige Kategorie von innen JAR wird:

public static Class[] getClasses(String packageName) 
throws ClassNotFoundException{
    ArrayList<Class> classes = new ArrayList<Class> ();

    packageName = packageName.replaceAll("\\." , "/");
    File f = new File(jarName);
    if(f.exists()){
        try{
            JarInputStream jarFile = new JarInputStream(
                    new FileInputStream (jarName));
            JarEntry jarEntry;

            while(true) {
                jarEntry=jarFile.getNextJarEntry ();
                if(jarEntry == null){
                    break;
                }
                if((jarEntry.getName ().startsWith (packageName)) &&
                        (jarEntry.getName ().endsWith (".class")) ) {
                    classes.add(Class.forName(jarEntry.getName().
                            replaceAll("/", "\\.").
                            substring(0, jarEntry.getName().length() - 6)));
                }
            }
        }
        catch( Exception e){
            e.printStackTrace ();
        }
        Class[] classesA = new Class[classes.size()];
        classes.toArray(classesA);
        return classesA;
    }else
        return null;
}

Hier ist ein Beispiel für die Verwendung Reflections Bibliothek rekursiv Scan Classpath durch regex Namensmuster mit ein paar Augmented von Guava Vergünstigungen zu Ressourcen Inhalt zu holen:

Reflections reflections = new Reflections("com.example.package", new ResourcesScanner());
Set<String> paths = reflections.getResources(Pattern.compile(".*\\.template$"));

Map<String, String> templates = new LinkedHashMap<>();
for (String path : paths) {
    log.info("Found " + path);
    String templateName = Files.getNameWithoutExtension(path);
    URL resource = getClass().getClassLoader().getResource(path);
    String text = Resources.toString(resource, StandardCharsets.UTF_8);
    templates.put(templateName, text);
}

Das funktioniert mit den beiden Gläsern und Explosionsklassen.

Ich habe portiert acheron55 der beantworten zu Java 7 und schlossen das FileSystem Objekt. Dieser Code funktioniert in IDE, in JAR-Dateien und in einem Glas in einem Krieg gegen den Tomcat 7; aber beachten Sie, dass es funktioniert nicht Arbeit in einem Glas in einem Krieg gegen den JBoss 7 (es gibt FileSystemNotFoundException: Provider "vfs" not installed, siehe auch dieser Beitrag ). Darüber hinaus wie der Original-Code ist es nicht Thread-sicher, wie errr . Aus diesen Gründen habe ich diese Lösung aufgegeben; jedoch, wenn Sie diese Probleme nicht annehmen können, hier ist mein fertiger Code:

import java.io.IOException;
import java.net.*;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collections;

public class ResourceWalker {

    public static void main(String[] args) throws URISyntaxException, IOException {
        URI uri = ResourceWalker.class.getResource("/resources").toURI();
        System.out.println("Starting from: " + uri);
        try (FileSystem fileSystem = (uri.getScheme().equals("jar") ? FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap()) : null)) {
            Path myPath = Paths.get(uri);
            Files.walkFileTree(myPath, new SimpleFileVisitor<Path>() { 
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    System.out.println(file);
                    return FileVisitResult.CONTINUE;
                }
            });
        }
    }
}

Es gibt zwei sehr nützlichen Dienstprogramme beide JarScan genannt:

  1. www.inetfeedback.com/jarscan

  2. jarscan.dev.java.net

Siehe auch diese Frage: JarScan, scannen alle JAR-Dateien in allen Unterordnern für bestimmte Klasse

Nur eine andere Art von Angebote / Dateien aus einem Glas URL zu lesen und es tut es rekursiv für verschachtelte Gläser

https://gist.github.com/trung/2cd90faab7f75b3bcbaa

URL urlResource = Thead.currentThread().getContextClassLoader().getResource("foo");
JarReader.read(urlResource, new InputStreamCallback() {
    @Override
    public void onFile(String name, InputStream is) throws IOException {
        // got file name and content stream 
    }
});
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top