Ява.НИО.файл.Путь к путь к классу ресурс
есть ли API для получения ресурса classpath (например, что я получу от Class.getResource(String)
) в качестве java.nio.file.Path
? В идеале я хотел бы использовать причудливые новые API-интерфейсы Path с ресурсами classpath.
8 ответов:
предполагая, что то, что вы хотите сделать, это вызвать файлы.русло.(..) на ресурсе, который поступает из 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(); }