Есть ли API для получения ресурса classpath (например, что я получил от Class.getResource(String)
) как java.nio.file.Path
? В идеале я бы хотел использовать новые API-интерфейсы Path с ресурсами класса.
Java.nio.file.Path для ресурса pathpath
Ответ 1
Это работает для меня:
return Paths.get(ClassLoader.getSystemResource(resourceName).toURI());
Ответ 2
Предполагая, что вы хотите сделать, вызывается Files.lines(...) на ресурсе, который исходит из пути к классам - возможно, изнутри jar.
Так как Oracle свернуло понятие о том, когда путь - это путь, не делая getResource возвращать полезный путь, если он находится в файле jar, то вам нужно сделать следующее:
Stream<String> stream = new BufferedReader(new InputStreamReader(ClassLoader.getSystemResourceAsStream("/filename.txt"))).lines();
Ответ 3
Оказывается, вы можете сделать это с помощью встроенного поставщика Zip File System. Однако передача 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);
}
Обновить:
Было справедливо отмечено, что приведенный выше код содержит утечку ресурсов, поскольку код открывает новый объект FileSystem, но никогда не закрывает его. Наилучший подход - передать рабочий объект, похожий на Consumer, очень похоже на то, как это делает ответ Holgers. Откройте файловую систему ZipFS достаточно долго, чтобы работник мог делать все, что ему нужно, с путем (если работник не пытается сохранить объект пути для последующего использования), затем закройте файловую систему.
Ответ 4
Наиболее общим решением является следующее:
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
URI или хранилищем модулей Java 9s) или открывать и, таким образом, безопасно закрывать файловую систему самостоятельно ( как файлы 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 9s нарушил приведенный выше пример кода. 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);
}
});
Затем он снова будет работать со всеми версиями и методами хранения.
Ответ 5
Я написал небольшой вспомогательный метод для чтения Paths
из ваших ресурсов класса. Это весьма удобно использовать, поскольку ему нужна только ссылка класса, который вы сохранили, а также имя самого ресурса.
public static Path getResourcePath(Class<?> resourceClass, String resourceName) throws URISyntaxException {
URL url = resourceClass.getResource(resourceName);
return Paths.get(url.toURI());
}
Ответ 6
Вы не можете создать URI из ресурсов внутри файла jar. Вы можете просто записать его в файл temp, а затем использовать его (java8):
Path path = File.createTempFile("some", "address").toPath();
Files.copy(ClassLoader.getSystemResourceAsStream("/path/to/resource"), path, StandardCopyOption.REPLACE_EXISTING);
Ответ 7
Вам необходимо определить файловую систему для чтения ресурсов из файла 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)) {
....
}
}
Ответ 8
Прочитайте файл из папки ресурсов с помощью 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();
}