Как мне прочитать файл ресурса из файла jar Java? - PullRequest
56 голосов
/ 31 декабря 2008

Я пытаюсь получить доступ к XML-файлу внутри jar-файла из отдельного jar-файла, работающего как настольное приложение. Я могу получить URL-адрес нужного мне файла, но когда я передаю его в FileReader (в виде строки), я получаю исключение FileNotFoundException, говорящее «Неверный синтаксис имени файла, имени каталога или метки тома».

Для справки, у меня нет проблем с чтением ресурсов изображения из того же jar-файла, передачей URL в конструктор ImageIcon. Кажется, это указывает на то, что метод, который я использую для получения URL-адреса, является правильным.

URL url = getClass().getResource("/xxx/xxx/xxx/services.xml");
ServicesLoader jsl = new ServicesLoader( url.toString() );

Внутри класса ServicesLoader у меня есть

XMLReader xr = XMLReaderFactory.createXMLReader();
xr.setContentHandler( this );
xr.setErrorHandler( this );
xr.parse( new InputSource( new FileReader( filename )));

Что не так с использованием этой техники для чтения файла XML?

Ответы [ 9 ]

59 голосов
/ 31 декабря 2008

Похоже, вы хотите использовать java.lang.Class.getResourceAsStream(String), см.

http://java.sun.com/javase/6/docs/api/java/lang/Class.html#getResourceAsStream(java.lang.String)

5 голосов
/ 31 декабря 2008

Вы не говорите, если это настольное или веб-приложение. Я бы использовал метод getResourceAsStream() из соответствующего ClassLoader, если это рабочий стол или Context, если это веб-приложение.

4 голосов
/ 11 февраля 2009

Проблема заключалась в том, что я зашел слишком далеко в вызове метода разбора XMLReader. Метод parse принимает InputSource, поэтому не было никакой причины даже использовать FileReader. Изменение последней строки кода выше на

xr.parse( new InputSource( filename ));

работает просто отлично.

4 голосов
/ 31 декабря 2008

Выглядит так, как будто вы используете результат URL.toString в качестве аргумента для конструктора FileReader. URL.toString немного сломан, и вместо этого вы обычно должны использовать url.toURI().toString(). В любом случае строка не является путем к файлу.

Вместо этого вы должны либо:

  • Передайте URL на ServicesLoader и позвольте ему вызвать openStream или подобное.
  • Используйте Class.getResourceAsStream и просто пропустите поток, возможно внутри InputSource. (Не забудьте проверить наличие нулей, так как API немного запутан.)
2 голосов
/ 13 октября 2013

Я хотел бы отметить, что одна проблема заключается в том, что если одни и те же ресурсы находятся в нескольких JAR-файлах. Допустим, вы хотите прочитать /org/node/foo.txt, но не из одного файла, а из каждого файла JAR.

Я сталкивался с этой же проблемой несколько раз раньше. В JDK 7 я надеялся, что кто-то напишет файловую систему classpath, но, увы, пока нет.

Spring имеет класс Resource, который позволяет вам очень приятно загружать ресурсы classpath.

Я написал небольшой прототип, чтобы решить эту проблему чтения ресурсов из нескольких файлов jar. Прототип не обрабатывает каждый крайний случай, но он обрабатывает поиск ресурсов в каталогах, которые находятся в jar-файлах.

Я довольно давно использовал переполнение стека. Это второй ответ, который я помню, отвечая на вопрос, так что прости меня, если я пойду слишком долго (это моя природа).

Это программа чтения ресурсов-прототипов. Прототип лишен надежной проверки ошибок.

У меня есть два файла jar-прототипа, которые я настроил.

 <pre>
         <dependency>
              <groupId>invoke</groupId>
              <artifactId>invoke</artifactId>
              <version>1.0-SNAPSHOT</version>
          </dependency>

          <dependency>
               <groupId>node</groupId>
               <artifactId>node</artifactId>
               <version>1.0-SNAPSHOT</version>
          </dependency>

Каждый из файлов jar имеет файл в / org / node / с именем resource.txt.

Это всего лишь прототип того, как будет выглядеть обработчик с classpath: // У меня также есть resource.foo.txt в моих локальных ресурсах для этого проекта.

Он собирает их все и распечатывает.

   <code>

    package com.foo;

    import java.io.File;
    import java.io.FileReader;
    import java.io.InputStreamReader;
    import java.io.Reader;
    import java.net.URI;
    import java.net.URL;
    import java.util.Enumeration;
    import java.util.zip.ZipEntry;
    import java.util.zip.ZipFile;

    /**
    * Prototype resource reader.
    * This prototype is devoid of error checking.
    *
    *
    * I have two prototype jar files that I have setup.
    * <pre>
    *             <dependency>
    *                  <groupId>invoke</groupId>
    *                  <artifactId>invoke</artifactId>
    *                  <version>1.0-SNAPSHOT</version>
    *              </dependency>
    *
    *              <dependency>
    *                   <groupId>node</groupId>
    *                   <artifactId>node</artifactId>
    *                   <version>1.0-SNAPSHOT</version>
    *              </dependency>
    * 
* Каждый из jar-файлов имеет файл в / org / node / с именем resource.txt. *
* Это всего лишь прототип того, как будет выглядеть обработчик с classpath: // * У меня также есть resource.foo.txt в моих локальных ресурсах для этого проекта. *
* / открытый класс ClasspathReader { public static void main (String [] args) выдает Exception { / * Этот проект включает в себя два файла JAR, каждый из которых имеет ресурс, расположенный в / org / node / называется resource.txt. * / / * Пространство имен - это просто устройство, которое я использую, чтобы увидеть, есть ли файл в каталоге начинается с пространства имен. Думайте о пространстве имен как о расширении файла но это начало файла, а не конец. * / String namespace = "resource"; // someResource является classpath. Строка someResource = args.length> 0? args [0]: //"classpath:///org/node/resource.txt "; Работает с файлами "Путь к классам: /// орг / узел /"; // Также работает с каталогами URI someResourceURI = URI.create (someResource); System.out.println ("URI ресурса =" + someResourceURI); someResource = someResourceURI.getPath (); System.out.println ("ПУТЬ ресурса =" + someResource); boolean isDir =! someResource.endsWith (". txt"); / ** Ресурс Classpath никогда не может начинаться с начального слеша. * Логически они делают, но на самом деле вы должны раздеть это. * Это известное поведение ресурсов classpath. * Он работает с косой чертой, если ресурс не находится в файле JAR. * Итог, удаляя его, он всегда работает. * / if (someResource.startsWith ("/")) { someResource = someResource.substring (1); } / * Используйте ClassLoader для поиска всех ресурсов, которые имеют это имя. Ищите все ресурсы, которые соответствуют местоположению, которое мы ищем. * / Ресурсы перечисления = ноль; / * Сначала проверьте контекстный загрузчик классов. Всегда используйте это, если доступно. * / пытаться { ресурсы = . Thread.currentThread () getContextClassLoader () GetResources (someResource). } catch (Exex ex) { ex.printStackTrace (); } if (resources == null ||! resources.hasMoreElements ()) { resources = ClasspathReader.class.getClassLoader (). getResources (someResource); }// Теперь перебираем URL ресурсов из пути к классам while (resources.hasMoreElements ()) { URL resource = resources.nextElement (); / * если ресурс представляет собой файл, это просто означает, что мы можем использовать обычный механизм сканировать каталог. * / if (resource.getProtocol (). equals ("file")) { // если это файл, то мы можем обработать его обычным способом. handleFile (ресурс, пространство имен); Продолжить; } System.out.println («Ресурс» + ресурс); / * Разделите строку, которая выглядит следующим образом: баночка: файл! /Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar / орг / узел / в это /Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar и это / Орг / узел / * / String [] split = resource.toString (). Split (":"); String [] split2 = split [2] .split ("!"); String zipFileName = split2 [0]; String sresource = split2 [1]; System.out.printf («Имя файла после разделения zip =% s,» + "\ nresource in zip% s \ n", zipFileName, sresource); / * Откройте zip-файл. * / ZipFile zipFile = новый ZipFile (zipFileName); / * Перебирать записи. * / Записи перечисления = zipFile.entries (); while (records.hasMoreElements ()) { ZipEntry entry = records.nextElement (); / * Если это каталог, пропустите его. * / if (entry.isDirectory ()) { Продолжить; } String entryName = entry.getName (); System.out.printf ("имя записи zip% s \ n", entryName); / * Если он не начинается с нашей строки someResource тогда это не наш ресурс, так что продолжайте. * / if (! entryName.startsWith (someResource)) { Продолжить; } / * часть fileName из имени записи. * где /foo/bar/foo/bee/bar.txt, bar.txt - это файл * / String fileName = entryName.substring (entryName.lastIndexOf ("/") + 1); System.out.printf ("имя_файла% s \ n", имя_файла); / * Посмотрим, начинается ли файл с нашего пространства имен и заканчивается нашим расширением. * / 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 содержимое файла% s \ n ### \ n", entryName, builder); } catch (Exex ex) { ex.printStackTrace (); } } // используем запись, чтобы увидеть, является ли это файл «1.txt» // Чтение из байта с использованием file.getInputStream (entry) } } } / ** * Файл находился в файловой системе, а не в zip-файле, * это здесь для полноты для этого примера. * иначе. * ресурс @param* @param namespace * Исключения @throws * / приватный статический void handleFile (ресурс URL, пространство имен String) выдает исключение { System.out.println («Обрабатывать этот ресурс как файл» + ресурс); URI uri = resource.toURI (); Файл файл = новый файл (uri.getPath ()); if (file.isDirectory ()) { for (Файл childFile: file.listFiles ()) { if (childFile.isDirectory ()) { Продолжить; } String fileName = childFile.getName (); if (fileName.startsWith (пространство имен) && fileName.endsWith ("txt")) { try (FileReader reader = new FileReader (childFile)) { StringBuilder builder = new StringBuilder (); int ch = 0; while ((ch = reader.read ())! = -1) { builder.append ((char) ch); } System.out.printf ("fileName =% s \ n \ n #### \ n содержимое файла% s \ n ### \ n", childFile, builder); } catch (Exex ex) { ex.printStackTrace (); } } } } еще { Строка fileName = file.getName (); if (fileName.startsWith (пространство имен) && fileName.endsWith ("txt")) { try (FileReader Reader = новый FileReader (файл)) { StringBuilder builder = new StringBuilder (); int ch = 0; while ((ch = reader.read ())! = -1) { builder.append ((char) ch); } System.out.printf ("fileName =% s \ n \ n #### \ n содержимое файла% s \ n ### \ n", fileName, builder); } catch (Exex ex) { ex.printStackTrace (); } } } } }

Здесь вы можете увидеть более полный пример с примером вывода.

0 голосов
/ 11 июня 2019

Вот пример кода о том, как правильно прочитать файл внутри файла JAR (в данном случае текущий исполняемый файл JAR)

Просто измените исполняемый файл на путь к файлу JAR, если он не является текущим запущенным.

Затем измените filePath на путь к файлу, который вы хотите использовать внутри jar-файла. И.Е. если ваш файл в

someJar.jar \ IMG \ test.gif

. Установите для filePath значение «img \ test.gif»

File executable = new File(BrowserViewControl.class.getProtectionDomain().getCodeSource().getLocation().toURI());
JarFile jar = new JarFile(executable);
InputStream fileInputStreamReader = jar.getInputStream(jar.getJarEntry(filePath));
byte[] bytes = new byte[fileInputStreamReader.available()];

int sizeOrig = fileInputStreamReader.available();
int size = fileInputStreamReader.available();
int offset = 0;
while (size != 0){
    fileInputStreamReader.read(bytes, offset, size);
    offset = sizeOrig - fileInputStreamReader.available();
    size = fileInputStreamReader.available();
}
0 голосов
/ 02 августа 2013

У меня есть 2 файла CSV, которые я использую для чтения данных. Java-программа экспортируется как исполняемый файл JAR. Когда вы экспортируете его, вы поймете, что он не экспортирует ваши ресурсы вместе с ним.

Я добавил папку в проекте под названием data в eclipse. В этой папке я сохранил свои CSV-файлы.

Когда мне нужно сослаться на эти файлы, я делаю это так ...

private static final String ZIP_FILE_LOCATION_PRIMARY = "free-zipcode-database-Primary.csv";
private static final String ZIP_FILE_LOCATION = "free-zipcode-database.csv";

private static String getFileLocation(){
    String loc = new File("").getAbsolutePath() + File.separatorChar +
        "data" + File.separatorChar;
    if (usePrimaryZipCodesOnly()){              
        loc = loc.concat(ZIP_FILE_LOCATION_PRIMARY);
    } else {
        loc = loc.concat(ZIP_FILE_LOCATION);
    }
    return loc;
}

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

0 голосов
/ 10 апреля 2011

Если вы интенсивно используете ресурсы, вы можете рассмотреть возможность использования Commons VFS .

Также поддерживает: * Локальные файлы * FTP, SFTP * HTTP и HTTPS * Временные файлы "нормальная ФС поддерживается) * Zip, Jar и Tar (без сжатия, tgz или tbz2) * gzip и bzip2 * Ресурсы * ram - "ramdrive" * мим

Есть также JBoss VFS - но это не очень документировано.

0 голосов
/ 31 декабря 2008

Вне вашей техники, почему бы не использовать стандартный класс Java JarFile для получения нужных ссылок? Оттуда большинство ваших проблем должно уйти.

...