Ява.НИО.файл.Путь к путь к классу ресурс


есть ли API для получения ресурса classpath (например, что я получу от Class.getResource(String)) в качестве java.nio.file.Path? В идеале я хотел бы использовать причудливые новые API-интерфейсы Path с ресурсами classpath.

8 101

8 ответов:

это работает для меня:

return Paths.get(ClassLoader.getSystemResource(resourceName).toURI());

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

поскольку Oracle запутал понятие, когда путь является путем, не заставляя getResource возвращать полезный путь, если он находится в файле jar, вам нужно сделать что-то вроде этого:

Stream<String> stream = new BufferedReader(new InputStreamReader(ClassLoader.getSystemResourceAsStream("/filename.txt"))).lines();

получается, вы можете сделать это с помощью встроенного Zip file System provider. Однако передача URI ресурса непосредственно в Paths.get не будет работать; вместо этого необходимо сначала создать файловую систему zip для URI jar без имени записи, а затем обратиться к записи в этой файловой системе:

static Path resourceToPath(URL resource)
throws IOException,
       URISyntaxException {

    Objects.requireNonNull(resource, "Resource URL cannot be null");
    URI uri = resource.toURI();

    String scheme = uri.getScheme();
    if (scheme.equals("file")) {
        return Paths.get(uri);
    }

    if (!scheme.equals("jar")) {
        throw new IllegalArgumentException("Cannot convert to Path: " + uri);
    }

    String s = uri.toString();
    int separator = s.indexOf("!/");
    String entryName = s.substring(separator + 2);
    URI fileURI = URI.create(s.substring(0, separator));

    FileSystem fs = FileSystems.newFileSystem(fileURI,
        Collections.<String, Object>emptyMap());
    return fs.getPath(entryName);
}

наиболее общее решение заключается в следующем:

interface IOConsumer<T> {
    void accept(T t) throws IOException;
}
public static void processRessource(URI uri, IOConsumer<Path> action) throws IOException {
    try {
        Path p=Paths.get(uri);
        action.accept(p);
    }
    catch(FileSystemNotFoundException ex) {
        try(FileSystem fs = FileSystems.newFileSystem(
                uri, Collections.<String,Object>emptyMap())) {
            Path p = fs.provider().getPath(uri);
            action.accept(p);
        }
    }
}

главное препятствие состоит в том, чтобы иметь дело с двумя возможностями, либо с существующей файловой системой, которую мы должны использовать, но не закрывать (например, с file URIs или хранилище модулей Java 9), или необходимость открывать и, таким образом, безопасно закрывать файловую систему самостоятельно (например, zip/jar-файлы).

поэтому решение выше инкапсулирует фактическое действие в interface, регулирует оба случая, безопасно закрывая потом во втором случае, и работает от Java 7 до Java 10. Он проверяет, есть ли уже открытая файловая система перед открытием новой, поэтому он также работает в случае, если другой компонент вашего приложения уже открыл файловую систему для того же файла zip/jar.

он может быть использован во всех версиях Java, указанных выше, например, для перечисления содержимого пакета (java.lang в примере) как Path s, Вот так:

processRessource(Object.class.getResource("Object.class").toURI(), new IOConsumer<Path>() {
    public void accept(Path path) throws IOException {
        try(DirectoryStream<Path> ds = Files.newDirectoryStream(path.getParent())) {
            for(Path p: ds)
                System.out.println(p);
        }
    }
});

С Java 8 или новее, вы можете использовать лямбда выражения или ссылки на методы для представления фактического действия, например

processRessource(Object.class.getResource("Object.class").toURI(), path -> {
    try(Stream<Path> stream = Files.list(path.getParent())) {
        stream.forEach(System.out::println);
    }
});

сделать то же самое.


окончательный выпуск модульной системы Java 9 нарушил приведенный выше пример кода. JRE непоследовательно возвращает путь /java.base/java/lang/Object.class на Object.class.getResource("Object.class"), тогда как должно быть /modules/java.base/java/lang/Object.class. Это может быть исправлено путем добавления недостающих /modules/ когда родительский путь сообщается как несуществующий:

processRessource(Object.class.getResource("Object.class").toURI(), path -> {
    Path p = path.getParent();
    if(!Files.exists(p))
        p = p.resolve("/modules").resolve(p.getRoot().relativize(p));
    try(Stream<Path> stream = Files.list(p)) {
        stream.forEach(System.out::println);
    }
});

затем он снова будет работать со всеми версиями и метод хранения.

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

public static Path getResourcePath(Class<?> resourceClass, String resourceName) throws URISyntaxException {
    URL url = resourceClass.getResource(resourceName);
    return Paths.get(url.toURI());
}  

вы не можете создать URI из ресурсов внутри файла jar. Вы можете просто записать его в временный файл, а затем использовать его (java8):

Path path = File.createTempFile("some", "address").toPath();
Files.copy(ClassLoader.getSystemResourceAsStream("/path/to/resource"), path, StandardCopyOption.REPLACE_EXISTING);

вам нужно определить файловую систему для чтения ресурса из файла jar, как указано в https://docs.oracle.com/javase/8/docs/technotes/guides/io/fsp/zipfilesystemprovider.html. я успешно читаю ресурс из файла jar с приведенными ниже кодами:

Map<String, Object> env = new HashMap<>();
try (FileSystem fs = FileSystems.newFileSystem(uri, env)) {

        Path path = fs.getPath("/path/myResource");

        try (Stream<String> lines = Files.lines(path)) {
            ....
        }
    }

чтение файла из папки ресурсов с помощью NIO, в java8

public static String read(String fileName) {

        Path path;
        StringBuilder data = new StringBuilder();
        Stream<String> lines = null;
        try {
            path = Paths.get(Thread.currentThread().getContextClassLoader().getResource(fileName).toURI());
            lines = Files.lines(path);
        } catch (URISyntaxException | IOException e) {
            logger.error("Error in reading propertied file " + e);
            throw new RuntimeException(e);
        }

        lines.forEach(line -> data.append(line));
        lines.close();
        return data.toString();
    }