Как я могу перечислить файлы внутри файла JAR?



у меня есть этот код, который читает все файлы из каталога.



    File textFolder = new File("text_directory");

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


Он отлично работает. Он заполняет массив всеми файлами, которые заканчиваются ".txt "из каталога "text_directory".



как я могу прочитать содержимое каталога подобным образом внутри файл JAR?



так что я действительно хочу сделать, чтобы перечислить все изображения внутри моего файла JAR, так что я могу загрузить их с:



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


(это работает, потому что "CompanyLogo "является" жестко закодированным", но количество изображений внутри файла JAR может быть от 10 до 200 переменной длины.)



EDIT



так что я думаю, моя главная проблема будет: как знать имя файла JAR где живет мой основной класс?



конечно, я мог бы прочитать его с помощью java.util.Zip.



моя структура выглядит так:



они так:



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


прямо сейчас я могу загрузить, например "images / image01.ПНГ" через:



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


но только потому, что я знаю имя файла, для остальных я должен загружать их динамически.

629   13  

13 ответов:

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... */
}

обратите внимание, что в Java 7, Вы можете создать FileSystem из файла JAR (zip), а затем используйте механизмы обхода и фильтрации каталогов NIO для поиска по нему. Это облегчило бы написание кода, который обрабатывает банки и" взорванные " каталоги.

код, который работает как для IDE и .файлы jar:

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

Эриксона ответ прекрасно работал:

вот рабочий код.

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() ] );

и я просто изменить мой метод загрузки из этого:

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

для этого:

String  [] webimages = ...

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

Я хотел бы расширить на acheron55 в ответ, так как это очень небезопасное решение, по нескольким причинам:

  1. он не закрывает

Так что я думаю, моя главная проблема будет, как узнать имя jar, где живет мой основной класс.

предполагая, что ваш проект упакован в банку (не обязательно верно!), вы можете использовать загрузчик классов.getResource() или findResource () с именем класса (далее .класс), чтобы получить банку, которая содержит данный класс. Вам нужно будет разобрать имя jar из URL-адреса, который возвращается (не так сложно), который я оставлю в качестве упражнения для читателя :-)

обязательно проверьте для случая, когда класс не является частью jar.

вот метод, который я написал для "run all JUnits under a package". Вы должны быть в состоянии адаптировать его к вашим потребностям.

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

изменить: Ах, в этом случае, вы можете также захотеть этот фрагмент (тот же случай использования :) )

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

файл jar - это просто zip-файл со структурированным манифестом. Вы можете открыть файл jar с помощью обычных инструментов Java zip и сканировать содержимое файла таким образом, раздувать потоки и т. д. Затем используйте это в вызове getResourceAsStream, и это должно быть все hunky dory.

редактировать / после уточнения

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

учитывая фактический файл JAR, вы можете перечислить содержимое с помощью JarFile.entries(). Однако вам нужно будет знать местоположение файла JAR - вы не можете просто попросить загрузчик классов перечислить все, что он может получить.

вы должны быть в состоянии определить местоположение файла JAR на основе URL, возвращенного из ThisClassName.class.getResource("ThisClassName.class"), но это может быть немного неудобный.

некоторое время назад я сделал функцию, которая получает classess изнутри JAR:

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

пример использования размышления библиотека для рекурсивного сканирования classpath по шаблону имени регулярного выражения, дополненному парой гуавы льготы для извлечения содержимого ресурсов:

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

это работает как с jars, так и с разнесенными классами.

есть две очень полезные утилиты, которые называются JarScan:

  1. www.inetfeedback.com/jarscan

  2. jarscan.dev.java.net

см. Также этот вопрос: JarScan, сканировать все файлы JAR во всех подпапках для конкретного класса

я портировал ответ acheron55 на Java 7 и закрыл

просто другой способ перечисления / чтения файлов из URL-адреса jar, и он делает это рекурсивно для вложенных jars

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

Comments

    Ничего не найдено.