Читання файлу ресурсів зсередини банку


242

Я хотів би прочитати ресурс з моєї баночки так:

File file;
file = new File(getClass().getResource("/file.txt").toURI());
BufferredReader reader = new BufferedReader(new FileReader(file));

//Read the file

і він працює добре, коли запускає його в Eclipse, але якщо я експортую його в банку, запустіть його, є IllegalArgumentException:

Exception in thread "Thread-2"
java.lang.IllegalArgumentException: URI is not hierarchical

і я дійсно не знаю, чому, але з деяким тестуванням я виявив, що змінити

file = new File(getClass().getResource("/file.txt").toURI());

до

file = new File(getClass().getResource("/folder/file.txt").toURI());

тоді він працює навпаки (працює в банку, але не затемнення).

Я використовую Eclipse, і папка з моїм файлом знаходиться в папці класу.


Якщо ви хочете читати файли з каталогу в банці з будь-якими номерами для файлів, див. Stackoverflow-Link
Michael Hegner

1
Я не впевнений, що оригінальне питання стосувалося Весни. Посилання в попередньому коментарі стосується весняної конкретної відповіді з іншого запитання. Я вважаю, що getResourceAsStreamце все ж більш просте і портативне рішення проблеми.
Дрю Макінніс

Відповіді:


398

Замість того, щоб намагатися звертатися до ресурсу як Файл, просто попросіть ClassLoader повернути InputStream для ресурсу замість цього через getResourceAsStream :

InputStream in = getClass().getResourceAsStream("/file.txt"); 
BufferedReader reader = new BufferedReader(new InputStreamReader(in));

Поки file.txtресурс доступний на classpath, тоді цей підхід буде працювати однаково незалежно від того, знаходиться file.txtресурс у classes/каталозі чи всередині jar.

URI is not hierarchicalВідбувається тому , що URI для ресурсу всередині файлу банки буде виглядати приблизно так: file:/example.jar!/file.txt. Ви не можете читати записи в jar( zipфайлі) так, як це був звичайний старий файл .

Це добре пояснюється відповідями на:


3
Дякую, це було дуже корисно, і код працює відмінно, але у мене є одна проблема, мені потрібно визначити, чи InputStreamіснує (як File.exists()), щоб моя гра могла сказати, чи слід використовувати файл за замовчуванням чи ні. Дякую.
PrinceCJC

1
О та BTW причина getClass().getResource("**/folder**/file.txt")змусила його працювати в тому, що я мав цю папку в тому ж каталозі, що і мій jar :).
PrinceCJC

5
getResourceAsStreamповертає null, якщо ресурс не існує, щоб ви могли бути вашим тестом "існує".
Дрю Макінніс

1
До речі, у вас помилка друку: це повинен бути BufferedReader, а не BufferredReader (зайві 'r'
помітіть

2
І звичайно ... не забудьте закрити inputStream та BufferedReader
Noremac

26

Щоб отримати доступ до файлу в банку, у вас є два варіанти:

  • Розмістіть файл у структурі каталогів, яка відповідає імені вашого пакета (після вилучення .jar-файлу він повинен знаходитися в тій самій папці, що і .class-файл), а потім перейдіть до нього за допомогою getClass().getResourceAsStream("file.txt")

  • Помістіть файл у корінь (після вилучення .jar-файлу він повинен бути в корені), потім перейдіть до нього за допомогою Thread.currentThread().getContextClassLoader().getResourceAsStream("file.txt")

Перший варіант може не працювати, коли jar використовується як плагін.


Дякую дуже за цю чудову відповідь ... Ще одна перевага від другого варіанту полягає в тому, що він також працює від основного методу, оскільки getClass () працює лише з екземпляра, а не зі статичних методів.
Дан Ортега

6

У мене була ця проблема раніше, і я створив резервний спосіб завантаження. По суті, перший спосіб працює в .jar-файлі, а другий працює в затемненні або іншому IDE.

public class MyClass {

    public static InputStream accessFile() {
        String resource = "my-file-located-in-resources.txt";

        // this is the path within the jar file
        InputStream input = MyClass.class.getResourceAsStream("/resources/" + resource);
        if (input == null) {
            // this is how we load file within editor (eg eclipse)
            input = MyClass.class.getClassLoader().getResourceAsStream(resource);
        }

        return input;
    }
}

3

Дотепер (грудень 2017 року) це єдине знайдене нами рішення, яке працює як усередині, так і зовні IDE.

Використовуйте PathMatchingResourcePatternResolver

Примітка: вона працює також у весняному завантаженні

У цьому прикладі я читаю деякі файли, розташовані в src / main / ресурси / my_folder :

try {
    // Get all the files under this inner resource folder: my_folder
    String scannedPackage = "my_folder/*";
    PathMatchingResourcePatternResolver scanner = new PathMatchingResourcePatternResolver();
    Resource[] resources = scanner.getResources(scannedPackage);

    if (resources == null || resources.length == 0)
        log.warn("Warning: could not find any resources in this scanned package: " + scannedPackage);
    else {
        for (Resource resource : resources) {
            log.info(resource.getFilename());
            // Read the file content (I used BufferedReader, but there are other solutions for that):
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(resource.getInputStream()));
            String line = null;
            while ((line = bufferedReader.readLine()) != null) {
                // ...
                // ...                      
            }
            bufferedReader.close();
        }
    }
} catch (Exception e) {
    throw new Exception("Failed to read the resources folder: " + e.getMessage(), e);
}

3

Проблема полягає в тому, що для певних сторонніх бібліотек потрібні імена файлів, а не потоки введення. Більшість відповідей не стосується цього питання.

У цьому випадку одним із вирішень є копіювання вмісту ресурсу у тимчасовий файл. У наступному прикладі використовуються jUnit TemporaryFolder.

    private List<String> decomposePath(String path){
        List<String> reversed = Lists.newArrayList();
        File currFile = new File(path);
        while(currFile != null){
            reversed.add(currFile.getName());
            currFile = currFile.getParentFile();
        }
        return Lists.reverse(reversed);
    }

    private String writeResourceToFile(String resourceName) throws IOException {
        ClassLoader loader = getClass().getClassLoader();
        InputStream configStream = loader.getResourceAsStream(resourceName);
        List<String> pathComponents = decomposePath(resourceName);
        folder.newFolder(pathComponents.subList(0, pathComponents.size() - 1).toArray(new String[0]));
        File tmpFile = folder.newFile(resourceName);
        Files.copy(configStream, tmpFile.toPath(), REPLACE_EXISTING);
        return tmpFile.getAbsolutePath();
    }

Це засклено багато. Дякуємо, що задокументували це. Мені цікаво, які інші варіанти існують без JUnit.
Алекс Мур-Ніємі

0

Якщо ви хочете читати як файл, я вважаю, що все ще існує подібне рішення:

    ClassLoader classLoader = getClass().getClassLoader();
    File file = new File(classLoader.getResource("file/test.xml").getFile());

4
URL.getFile () не перетворює URL у ім'я файлу. Він повертає частину URL-адреси після хоста з усіма відсотковими кодуваннями неушкодженими, тому якщо шлях містить будь-які символи, що не належать до ASCII, або будь-які символи ASCII, не заборонені в URL-адресах (включаючи пробіли), то результат не буде існуючим ім'ям файлу , навіть якщо URL-адреса є file:URL-адресою.
VGR

48
це не спрацьовує всередині одного разу, коли програма збирається до баночки
Akshay Kasar

1
Не працює з jar, якщо спочатку не перетворити в рядок і зберегти його локально.
smoosh911

0

Переконайтеся, що ви працюєте з правильним роздільником. Я замінив усіх /у відносному шляху на a File.separator. Це добре працювало в IDE, однак не працювало в JAR збірки.


0

Я думаю, що це також має працювати в Java. Наступний код, який я використовую, використовує kotlin.

val resource = Thread.currentThread().contextClassLoader.getResource('resources.txt')

0

Я знайшов виправлення

BufferedReader br = new BufferedReader(new InputStreamReader(Main.class.getResourceAsStream(path)));

Замініть "Main" класом java, в якому ви його зашифрували. Замініть "path" на шлях у файлі jar.

Наприклад, якщо ви помістите State1.txt в пакет com.issac.state, то введіть шлях як "/ com / issac / state / State1", якщо ви запускаєте Linux або Mac. Якщо ви запускаєте Windows, тоді введіть шлях як "\ com \ issac \ state \ State1". Не додайте розширення .txt у файл, якщо не трапляється виняток Файл.


-1

Ви можете використовувати завантажувач класів, який буде читати з classpath як шлях ROOT (без "/" на початку)

InputStream in = getClass().getClassLoader().getResourceAsStream("file.txt"); 
BufferedReader reader = new BufferedReader(new InputStreamReader(in));

-1

Якщо ви використовуєте spring, тоді ви можете використовувати наступний метод для читання файлу з src / main / ресурси:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.springframework.core.io.ClassPathResource;

  public String readFileToString(String path) throws IOException {

    StringBuilder resultBuilder = new StringBuilder("");
    ClassPathResource resource = new ClassPathResource(path);

    try (
        InputStream inputStream = resource.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {

      String line;

      while ((line = bufferedReader.readLine()) != null) {
        resultBuilder.append(line);
      }

    }

    return resultBuilder.toString();
  }

1
Ласкаво просимо до SO. Це не дає відповіді на запитання. Коли у вас буде достатня репутація, ви зможете коментувати будь-яку публікацію. Перевірте також, що я можу зробити замість цього .
thewaywewere

Це видалить розриви рядків у файлі!
elad.chen

-1

Чомусь classLoader.getResource()завжди повертався null, коли я розгортав веб-додаток на WildFly 14. отримуючи classLoader з getClass().getClassLoader()або Thread.currentThread().getContextClassLoader()повертаючи null.

getClass().getClassLoader() Документ API каже:

"Повертає завантажувач класу для класу. Деякі реалізації можуть використовувати null для представлення завантажувача класу bootstrap. Цей метод поверне null в таких реалізаціях, якщо цей клас завантажений завантажувачем класу bootstrap."

можливо, якщо ви використовуєте WildFly і ваш веб-додаток, спробуйте це

request.getServletContext().getResource()повернув URL-адресу ресурсу. Тут запит є об’єктом ServletRequest.


-2

Нижче код працює з Spring boot (kotlin):

val authReader = InputStreamReader(javaClass.getResourceAsStream("/file1.json"))
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.