Чи існує API, щоб отримати ресурс classpath (наприклад, з чого я отримав Class.getResource(String)
) як java.nio.file.Path
? В ідеалі, я хотів би використовувати нові фантазії Path
API з ресурсами classpath.
Чи існує API, щоб отримати ресурс classpath (наприклад, з чого я отримав Class.getResource(String)
) як java.nio.file.Path
? В ідеалі, я хотів би використовувати нові фантазії Path
API з ресурсами classpath.
Відповіді:
Цей для мене працює:
return Paths.get(ClassLoader.getSystemResource(resourceName).toURI());
Thread.currentThread().getContextClassLoader().getResource(resourceName).toURI()
Здогадуючись, що те, що ви хочете зробити, - це зателефонувати Files.lines (...) на ресурс, що надходить із classpath - можливо, зсередини банку.
Оскільки Oracle суперечив поняттю, коли Path - це Шлях, не змушуючи getResource повертати корисний шлях, якщо він знаходиться у файлі jar, то вам потрібно зробити щось подібне:
Stream<String> stream = new BufferedReader(new InputStreamReader(ClassLoader.getSystemResourceAsStream("/filename.txt"))).lines();
class.getResource
потрібна коса коса риса, але я getSystemResourceAsStream
не можу знайти файл із префіксом косою рисою.
Найбільш загальне рішення полягає в наступному:
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 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);
}
});
Потім він знову працюватиме з усіма версіями та методами зберігання.
Виявляється, ви можете це зробити за допомогою вбудованого постачальника 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, але ніколи не закриває його. Найкращим підходом є передача об'єкта, що нагадує споживача, як і відповідь Хольгера. Відкрийте файлову систему ZipFS достатньо довго, щоб працівник міг робити все, що потрібно робити із Шляхом (доки працівник не намагатиметься зберегти об'єкт Path для подальшого використання), а потім закрийте FileSystem.
newFileSystem
може призвести до безлічі ресурсів, що навішуються відкритими назавжди. Хоча додаток @raisercostin дозволяє уникнути помилки при спробі створити вже створену файлову систему, якщо ви спробуєте використати повернуті, Path
ви отримаєте ClosedFileSystemException
. @Holger відповідь добре працює для мене.
FileSystem
. Якщо ви завантажуєте ресурс з Jar, а потім створюєте необхідні FileSystem
- це FileSystem
також дозволить вам завантажувати інші ресурси з того ж Jar. Крім того, щойно ви створили нове, FileSystem
ви можете просто спробувати завантажити ресурс знову за допомогою, Paths.get(Path)
і реалізація автоматично використовуватиме новий FileSystem
.
#getPath(String)
метод на FileSystem
об’єкті.
Я написав маленький помічник-метод для читання 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);
Прочитайте файл з папки ресурсів за допомогою 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();
}
Вам потрібно визначити Filesystem, щоб читати ресурс з файлу 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)) {
....
}
}
Paths.get(URI)
, а потім ´URL.toURI (), and last
getResource () `, який повертає aURL
. Можливо, ви зможете це з'єднати. Не намагався, хоча.