Достъп до външни .jar ресурси от java

Работя върху многомодулен Java проект, той се състои от множество проекти в Eclipse, които зависят един от друг, сега, за да добавя поддръжка на GUI тема, създадох Java проект, който не съдържа никакъв код, само иконите и снимките, необходими за GUI, направих го Java проект, така че Maven ще го вгради в .jar. Сега има основно приложение, което зарежда множеството проекти в основен GUI, който получава своите икони от ресурси в реалните модули в момента. Всичко, което искам да направя, е да заредя всички тези ресурси от външния фиктивен .jar, който е включен в класовата пътека на .jar на основното приложение. Всеки метод, който открих досега, изглежда не работи. .jar не съдържа действителни java .classes, така че няма ClassLoader за справка. Има ли други методи за зареждане на снимките без извличане на .jar?


person user1307791    schedule 16.04.2012    source източник
comment
Този въпрос изглежда като дубликат на stackoverflow.com/questions/403256/   -  person Kappei    schedule 16.04.2012
comment
той .jar не съдържа действителни java .classes, така че няма ClassLoader за справка. Ако Jar е на пътя на класа по време на изпълнение на приложението, ресурсите трябва да бъдат намерени с помощта на getResource( ) (в инструмента за зареждане на контекстни класове) с „път от root“, като например /path/to/the.png   -  person Andrew Thompson    schedule 16.04.2012
comment
Това всъщност беше проста грешка от моя страна (използвах несъществуващ път). Очевидно Java няма проблем с грабването на ресурси от classpath, без значение откъде се извиква getResource(). Някой ми обясни, че Java основно изгражда файлова система от всички използвани буркани, когато се изпълнява, така че достъпът е доста прост и глобално достъпен. Благодаря за предложенията на всички!   -  person user1307791    schedule 20.04.2012


Отговори (3)


Три неща, които можете да опитате:

  1. Създайте фиктивен клас в буркана с ресурси, който можете да използвате, за да получите справката за ClassLoader.
  2. Използвайте URLClassLoader, ако знаете къде се намира вашият jar-файл.
  3. Винаги имате възможност да създадете свой собствен ClassLoader чрез разширяване на java.lang.ClassLoader.
person henrik    schedule 16.04.2012

Третирайте външния jar файл като zip архив и прочетете изображенията/ресурса нещо подобно:

public ZipStuff(String filename) throws IOException
{
    ZipFile zf = new ZipFile(filename);
    Enumeration<? extends ZipEntry> entries = zf.entries();

    while (entries.hasMoreElements()) {
      ZipEntry ze = entries.nextElement();
      ImageIcon ii = new ImageIcon(ImageIO.read(zf.getInputStream(ze)));
      JLabel l = new JLabel(ii);
      getContentPane().add(l);
    }
    setVisible(true);
    pack();
}

В този случай имам jar файл с едно единствено изображение, което зареждам в JLabel. Класът ZipStuff е JFrame.

person Kennet    schedule 16.04.2012

Можете да получите всички ресурси (всички jar файлове на classpath трябва да работят дори един без класове):

    Enumeration<URL> resources = null;
    try {
        resources = Thread.currentThread().getContextClassLoader().getResources(someResource);
    } catch (Exception ex) {
         //no op      
    }
    if (resources == null || !resources.hasMoreElements()) {
        resources = ClasspathReader.class.getClassLoader().getResources(someResource);
    }

След това проверете дали текущият ресурс е файл. Файлове, с които можете да работите директно като файлове. Но вашият въпрос беше за jar файлове, така че няма да отида там.

    while (resources.hasMoreElements()) {
        URL resource = resources.nextElement();


        if (resource.getProtocol().equals("file")) {
            //if it is a file then we can handle it the normal way.
            handleFile(resource, namespace);
            continue;
        }

В този момент трябва да имате само jar:file ресурси, така че... Разделете низа, който изглежда така:

jar:file:/Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar!/org/node/

в това

/Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar

и този

/org/node/

Ето кода за извършване на горното без досадна проверка за грешки. :)

        String[] split = resource.toString().split(":");
        String[] split2 = split[2].split("!");
        String zipFileName = split2[0];
        String sresource = split2[1];

        System.out.printf("After split zip file name = %s," +
                " \nresource in zip %s \n", zipFileName, sresource);

Сега имаме името на zip файла, за да можем да го прочетем:

        ZipFile zipFile = new ZipFile(zipFileName);

Сега можем да преминем през неговите записи:

        Enumeration<? extends ZipEntry> entries = zipFile.entries();

        while (entries.hasMoreElements()) {
            ZipEntry entry = entries.nextElement();

            /* If it is a directory, then skip it. */
            if (entry.isDirectory()) {
                continue;
            }

            String entryName = entry.getName();
            System.out.printf("zip entry name %s \n", entryName);

Вижте дали започва с ресурса, който търсим.

            if (!entryName.startsWith(someResource)) {
                continue;
            }

Имаше два трика, които направих по-рано, за да видя дали това е директория

    boolean isDir = !someResource.endsWith(".txt");

Това работи само защото търся ресурси, които завършват с .txt и предполагам, че ако не завършва с .txt, това е dir /foo/dir и /foo/dir/ и двете работят.

Другият трик беше следният:

    if (someResource.startsWith("/")) {
        someResource = someResource.substring(1);
    }

Ресурсът Classpath никога не може наистина да започне с начална наклонена черта. Логично го правят, но в действителност трябва да го съблечете. Това е известно поведение на ресурсите на classpath. Работи с наклонена черта, освен ако ресурсът не е в jar файл. В крайна сметка, като го оголи, винаги работи.

Трябва да получим действителното име на файла на проклетото нещо. Частта fileName от името на записа. където /foo/bar/foo/bee/bar.txt и искаме 'bar.txt', което е името на файла. Обратно в нашия цикъл while.

        while (entries.hasMoreElements()) {
            ZipEntry entry = entries.nextElement();
            ...
            String entryName = entry.getName(); //entry is zipEntry
            String fileName = entryName.substring(entryName.lastIndexOf("/") + 1);


            /** See if the file starts with our namespace and ends with our extension. */
            if (fileName.startsWith(namespace) && fileName.endsWith(".txt")) {

След това виждаме дали тези записи отговарят на нашите критерии и ако е така, прочитаме съдържанието на файла в System.out.

          try (Reader reader = new InputStreamReader(zipFile.getInputStream(entry))) {
                    StringBuilder builder = new StringBuilder();
                    int ch = 0;
                    while ((ch = reader.read()) != -1) {
                        builder.append((char) ch);

                    }
                    System.out.printf("zip fileName = %s\n\n####\n contents of file %s\n###\n", 
                    entryName, builder);
                } catch (Exception ex) {
                    ex.printStackTrace();//it is an example/proto :)
                }
            }

Можете да видите пълния пример тук: Sleepless in Pleasanton.

person RickHigh    schedule 13.10.2013