tutorial - java.nio.file.Path para un recurso classpath
java nio tutorial (8)
Adivinando que lo que quiere hacer es llamar a Files.lines (...) en un recurso que proviene del classpath, posiblemente desde dentro de un jar.
Dado que Oracle enredado la noción de cuando una ruta de acceso es una ruta de acceso al no hacer que getResource devuelva una ruta utilizable si se encuentra en un archivo jar, lo que debe hacer es algo como esto:
Stream<String> stream = new BufferedReader(new InputStreamReader(ClassLoader.getSystemResourceAsStream("/filename.txt"))).lines();
¿Hay una API para obtener un recurso de ruta de clases (por ejemplo, lo que obtendría de Class.getResource(String)
) como java.nio.file.Path
? Idealmente, me gustaría utilizar las nuevas y sofisticadas API Path con recursos classpath.
Debe definir el Sistema de archivos para leer el recurso del archivo jar como se menciona en https://docs.oracle.com/javase/8/docs/technotes/guides/io/fsp/zipfilesystemprovider.html . Tengo éxito al leer el recurso del archivo jar con los códigos a continuación:
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)) {
....
}
}
Escribí un pequeño método de ayuda para leer Paths
de los recursos de tu clase. Es bastante útil usarlo, ya que solo necesita una referencia de la clase en la que ha almacenado sus recursos, así como el nombre del recurso en sí.
public static Path getResourcePath(Class<?> resourceClass, String resourceName) throws URISyntaxException {
URL url = resourceClass.getResource(resourceName);
return Paths.get(url.toURI());
}
Esta funciona para mí:
return Paths.get(ClassLoader.getSystemResource(resourceName).toURI());
La solución más general es la siguiente:
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);
}
}
}
El principal obstáculo es tratar las dos posibilidades, tener un sistema de archivos existente que deberíamos usar, pero no cerrar (como con file
URI de file
o el almacenamiento de módulos de Java 9), o tener que abrir y cerrar el sistema de archivos nosotros mismos de forma segura ( como archivos zip / jar).
Por lo tanto, la solución anterior encapsula la acción real en una interface
, maneja ambos casos, se cierra de manera segura en el segundo caso y funciona desde Java 7 a Java 10. Detecta si ya hay un sistema de archivos abierto antes de abrir uno nuevo, por lo que también funciona en el caso de que otro componente de su aplicación ya haya abierto un sistema de archivos para el mismo archivo zip / jar.
Se puede usar en todas las versiones de Java mencionadas anteriormente, por ejemplo, para enumerar los contenidos de un paquete ( java.lang
en el ejemplo) como Path
s, como este:
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);
}
}
});
Con Java 8 o posterior, puede usar expresiones lambda o referencias de métodos para representar la acción real, por ej.
processRessource(Object.class.getResource("Object.class").toURI(), path -> {
try(Stream<Path> stream = Files.list(path.getParent())) {
stream.forEach(System.out::println);
}
});
hacer lo mismo.
La versión final del sistema de módulos de Java 9 ha roto el ejemplo de código anterior. El JRE devuelve /java.base/java/lang/Object.class
la ruta /java.base/java/lang/Object.class
para Object.class.getResource("Object.class")
mientras que debe ser /modules/java.base/java/lang/Object.class
. Esto puede solucionarse anteponiendo los /modules/
faltantes /modules/
cuando la ruta principal se informa como inexistente:
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);
}
});
Luego, volverá a funcionar con todas las versiones y métodos de almacenamiento.
Lea un archivo de la carpeta de recursos usando NIO, en 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();
}
No puede crear URI a partir de recursos dentro del archivo jar. Simplemente puede escribirlo en el archivo temporal y luego usarlo (java8):
Path path = File.createTempFile("some", "address").toPath();
Files.copy(ClassLoader.getSystemResourceAsStream("/path/to/resource"), path, StandardCopyOption.REPLACE_EXISTING);
Resulta que puede hacerlo, con la ayuda del proveedor de sistema de archivos Zip incorporado. Sin embargo, pasar un URI de recurso directamente a Paths.get
no funcionará; en su lugar, primero se debe crear un sistema de archivos zip para el URI jar sin el nombre de la entrada, luego consultar la entrada en ese sistema de archivos:
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);
}