Как я могу получить ресурс «Папка» из моего файла JAR?

139

У меня есть папка / пакет ресурсов в корне моего проекта, я не хочу загружать определенный файл. Если бы я хотел загрузить определенный файл, я бы использовал class.getResourceAsStream, и я был бы в порядке !! Что я на самом деле хочу сделать, так это загрузить «Папку» в папке ресурсов, зациклить файлы внутри этой папки, получить поток для каждого файла и прочитать содержимое ... Предположим, что имена файлов не определены до времени выполнения ... Что я должен делать? Есть ли способ получить список файлов внутри папки в вашем файле jar? Обратите внимание, что файл Jar с ресурсами - это тот же файл JAR, из которого выполняется код ...

Заранее спасибо...

Мостафа Зейнали
источник
1
Это помогло бы: stackoverflow.com/questions/1529611/…
Джигар Джоши
Также смотрите: stackoverflow.com/questions/4764347/…
Джигар Джоши
5
Первый - это извлечение файла jar, что-то вроде последней вещи, которую я хочу попробовать, например, ситуация «хуже-хуже-хуже» !! Если я хочу извлечь файлы, я просто поместил бы их в мою папку установки во время установки! Я хочу получить к ним доступ изнутри jar, и я считаю, что если getResourceAsStream может получить доступ к файлу, Java также должна иметь возможность исследовать папки в этом jar без необходимости извлекать его !! Но спасибо ...
Мостафа Зейнали

Ответы:

111

Наконец я нашел решение:

final String path = "sample/folder";
final File jarFile = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath());

if(jarFile.isFile()) {  // Run with JAR file
    final JarFile jar = new JarFile(jarFile);
    final Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
    while(entries.hasMoreElements()) {
        final String name = entries.nextElement().getName();
        if (name.startsWith(path + "/")) { //filter according to the path
            System.out.println(name);
        }
    }
    jar.close();
} else { // Run with IDE
    final URL url = Launcher.class.getResource("/" + path);
    if (url != null) {
        try {
            final File apps = new File(url.toURI());
            for (File app : apps.listFiles()) {
                System.out.println(app);
            }
        } catch (URISyntaxException ex) {
            // never happens
        }
    }
}

Второй блок просто работает, когда вы запускаете приложение в IDE (не с файлом JAR). Вы можете удалить его, если вам это не нравится.

user1079877
источник
Обновил правильный ответ. Хотя это не то, что я хотел бы сделать, особенно в коде с 34000+ исходных файлов ... Но, похоже, это единственное решение. Спасибо.
Мостафа Зейнали
4
К вашему сведению, это не работает, если запущено из веб-запуска, потому что getPath возвращает что-то относительно домена сервера.
amos
1
Не думайте, что это сработает, если путь указан в jar-файле. Это приведет к ошибке при преобразовании toURI: java.lang.IllegalArgumentException: URI не является иерархическим
tribbloid
4
Работает только для файла с одним jar, не работает для извлечения ресурсов из зависимостей в другом jar
tribbloid
2
Это не безопасное решение, потому что: 1. Предполагается, что файл .jar является локальным файлом в той же системе; 2. URL.getPath () не возвращает правильное имя файла, он просто возвращает часть пути URL-адреса со всеми процентами-выходами без изменений; 3. ProtectionDomain.getCodeSource () может возвращать ноль .
VGR
14

Попробуйте следующее.
Сделайте путь к ресурсу, "<PathRelativeToThisClassFile>/<ResourceDirectory>" например, если ваш путь к классу - com.abc.package.MyClass, а ваши файлы ресурсов находятся в src / com / abc / package / resources /:

URL url = MyClass.class.getResource("resources/");
if (url == null) {
     // error - missing folder
} else {
    File dir = new File(url.toURI());
    for (File nextFile : dir.listFiles()) {
        // Do something with nextFile
    }
}

Вы также можете использовать

URL url = MyClass.class.getResource("/com/abc/package/resources/");
Глен Бест
источник
27
Спасибо за потраченное время и усилия, но это НЕ работает, когда ваша кодовая база находится внутри файла .jar, а ваши ресурсы также находятся в этом файле .jar. Когда вы находитесь внутри файла .jar, вы не можете создать File (), а также не можете получить URL-адрес для этой папки ... Я лично с этим смирился и просто перечислил каждый файл в XML ... Я не Я думаю, что вы можете сделать это для папки внутри jar-файла ...
Мостафа Зейнали
2
Извините, что у вас проблемы. Тем не менее, он действительно работает, когда ваша кодовая база находится в файле JAR, а ваши ресурсы также находятся в этом файле JAR. Вы не создаете файл () на диске - вы читаете файл из файла JAR в память Java. Обратите внимание, что ClassLoader вполне может относиться к файлу JAR, эквивалентному каталогу на диске. Вот подробности из источника:
Глен Бест
1
Просто запустил код на банке и получил ошибку ... упс. Извини, мой плохой. Необходимо обработать путь URL-адреса jar с помощью «Файл: ...! / Dir1 / dir2 ...». Два исправления: stackoverflow.com/questions/1429172/… ИЛИ String jarPath = dirURL.getPath (). Substring (5, dirURL.getPath (). IndexOf ("!")); JarFile jar = новый JarFile (URLDecoder.decode (jarPath, "UTF-8")); Перечисление <JarEntry> records = jar.entries (); while (records.hasMoreElements ()) {// сделать что-то}
Глен Бест
3
Из javadoc очевидно, что метод ВСЕГДА не гарантирует возврат файла при любых обстоятельствах. НО, если вы действительно читаете Q, OP на 100% гарантированно будет работать с файлами и папками по построению. Вопрос просит получить доступ к каталогам и файлам - это более конкретно, чем обобщенная абстракция API. Преобразование в файл является подходящим и правильным для удовлетворения потребностей. Пожалуйста, внимательно прочитайте вопрос, прежде чем публиковать такие сильно противоречащие комментарии. Приветствия.
Глен Бест
7
ОП не работает с файлами и каталогами. Он работает с ресурсами в файле JAR. Ресурсы в файле JAR не являются файлами или каталогами и не могут быть перечислены вместе с Fileклассом. Этот код не работает с ресурсами внутри файла JAR. Период.
Маркиз Лорн
7

Я знаю, что это много лет назад. Но только для других людей попадается эта тема. Что вы можете сделать, это использовать getResourceAsStream()метод с путем к каталогу, и входной поток будет иметь все имена файлов из этого каталога. После этого вы можете объединить путь dir с каждым именем файла и вызвать getResourceAsStream для каждого файла в цикле.

Чэн Лин
источник
7
Это работает хорошо, если вы не планируете раздражать вещи, как выясняется.
Tustin2121
1
Я думаю, что это должно быть приемлемым решением, так как оно чистое и не требует обработки версий jar и IDE в отдельных случаях. Хотя я не уверен, почему getResource не находит папку, а getResourceAsStream делает.
Рагурам Онти Шринивасан
6

У меня была такая же проблема, когда я пытался загрузить некоторые конфигурации hadoop из ресурсов, упакованных в jar ... как в IDE, так и в jar (версия для выпуска).

Я обнаружил, java.nio.file.DirectoryStreamчто лучше всего перебирать содержимое каталога как в локальной файловой системе, так и в jar.

String fooFolder = "/foo/folder";
....

ClassLoader classLoader = foofClass.class.getClassLoader();
try {
    uri = classLoader.getResource(fooFolder).toURI();
} catch (URISyntaxException e) {
    throw new FooException(e.getMessage());
} catch (NullPointerException e){
    throw new FooException(e.getMessage());
}

if(uri == null){
    throw new FooException("something is wrong directory or files missing");
}

/** i want to know if i am inside the jar or working on the IDE*/
if(uri.getScheme().contains("jar")){
    /** jar case */
    try{
        URL jar = FooClass.class.getProtectionDomain().getCodeSource().getLocation();
        //jar.toString() begins with file:
        //i want to trim it out...
        Path jarFile = Paths.get(jar.toString().substring("file:".length()));
        FileSystem fs = FileSystems.newFileSystem(jarFile, null);
        DirectoryStream<Path> directoryStream = Files.newDirectoryStream(fs.getPath(fooFolder));
        for(Path p: directoryStream){
            InputStream is = FooClass.class.getResourceAsStream(p.toString()) ;
        performFooOverInputStream(is);
        /** your logic here **/
            }
    }catch(IOException e) {
        throw new FooException(e.getMessage());     
    }
}
else{
    /** IDE case */
    Path path = Paths.get(uri);
    try {
        DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path);
        for(Path p : directoryStream){
            InputStream is = new FileInputStream(p.toFile());
            performFooOverInputStream(is);
        }
    } catch (IOException _e) {
        throw new FooException(_e.getMessage());
    }
}
SQL.injection
источник
Это работало нормально для меня. Только я переключил 'Path jarFile = Paths.get (jar.toString (). Substring ("file:". Length ())); " с 'Path jarFile = Paths.get (jar.toURI ());' заставить его работать в Windows. Также используется оператор try with resource для объекта FileSystem.
Ярле Якобсен,
Это сработало для меня! Я запускал приложение dropwizard в докере.
nikunj
4

Следующий код возвращает искомую «папку» как путь, независимо от того, находится она внутри фляги или нет.

  private Path getFolderPath() throws URISyntaxException, IOException {
    URI uri = getClass().getClassLoader().getResource("folder").toURI();
    if ("jar".equals(uri.getScheme())) {
      FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap(), null);
      return fileSystem.getPath("path/to/folder/inside/jar");
    } else {
      return Paths.get(uri);
    }
  }

Требуется Java 7+.

hummingV
источник
Ницца! Я должен добавить, однако, что FileSystem является Closeable, что означает, что вы должны закрыть его в какой-то момент, чтобы освободить системные ресурсы. Конечно, возвращенный путь станет недействительным сразу после этого.
Кирилл Гамазков
Обратите внимание, что в случае файла JAR имя ресурса (здесь пакет + folder), по-видимому, игнорируется при создании файловой системы. Поэтому getPathне возвращается folder/path/to/..., а только path/to/....
Marcono1234
1

Другое решение, вы можете сделать это, используя ResourceLoaderэто:

import org.springframework.core.io.Resource;
import org.apache.commons.io.FileUtils;

@Autowire
private ResourceLoader resourceLoader;

...

Resource resource = resourceLoader.getResource("classpath:/path/to/you/dir");
File file = resource.getFile();
Iterator<File> fi = FileUtils.iterateFiles(file, null, true);
while(fi.hasNext()) {
    load(fi.next())
}
Qy Zuo
источник
0

Просто ... используйте OSGi. В OSGi вы можете перебирать записи вашего Bundle с помощью findEntries и findPaths.

Питер Криенс
источник
10
Если это касается OSGI, я бы вряд ли назвал это «простым». Но если вы уже используете OSGI ... да, конечно. Но предложение перейти в OSGI только для решения этой конкретной проблемы кажется слишком большим.
Крис
OSGi также решает множество других проблем, и в настоящее время очень легко начать работу. Смотрите учебные руководства по OSGi enRoute
Питер Криенс
Я бы держался подальше от OSGi, если вы уже не обязаны его использовать. Чрезмерно разработанное программное обеспечение Bloatware, которое решает проблемы развертывания IMO 90-х годов.
user2337270
-1

Внутри моего jar-файла была папка Upload, в этой папке было три других текстовых файла, и мне нужно было иметь точно такую ​​же папку и файлы за пределами jar-файла, я использовал код ниже:

URL inputUrl = getClass().getResource("/upload/blabla1.txt");
File dest1 = new File("upload/blabla1.txt");
FileUtils.copyURLToFile(inputUrl, dest1);

URL inputUrl2 = getClass().getResource("/upload/blabla2.txt");
File dest2 = new File("upload/blabla2.txt");
FileUtils.copyURLToFile(inputUrl2, dest2);

URL inputUrl3 = getClass().getResource("/upload/blabla3.txt");
File dest3 = new File("upload/Bblabla3.txt");
FileUtils.copyURLToFile(inputUrl3, dest3);
LoveLovelyJava
источник
1
Привет, спасибо за ответ, но, видите, вам нужно было знать имя каждого файла в этой папке при написании кода; и вы должны были назвать каждое из них явно. Я искал способ перебора файлов внутри папки и получения их имен во время выполнения, чтобы я мог добавлять ресурсы в папку, не изменяя код, зная, что код будет их обрабатывать. Спасибо за ваше время, хотя. : 3
Мостафа Зейнали
-1

Как указывают другие ответы, когда ресурсы находятся внутри jar-файла, все становится действительно безобразным. В нашем случае это решение:

https://stackoverflow.com/a/13227570/516188

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

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

Эммануэль Тузери
источник
-25

Эта ссылка говорит вам, как.

Волшебство - это метод getResourceAsStream ():

InputStream is = 
this.getClass().getClassLoader().getResourceAsStream("yourpackage/mypackage/myfile.xml")
Джеймс Андерсон
источник
20
Нет чувак!! Я не хочу ни одного файла! Я хочу целую папку, чьи подфайлы не определены до начала работы !!!
Мостафа Зейнали