クラスパスリソース(たとえば、私が取得するものClass.getResource(String)
)をとして取得するAPIはありますjava.nio.file.Path
か?理想的には、Path
クラスパスリソースで空想の新しいAPI を使用したいと思います。
クラスパスリソース(たとえば、私が取得するものClass.getResource(String)
)をとして取得するAPIはありますjava.nio.file.Path
か?理想的には、Path
クラスパスリソースで空想の新しいAPI を使用したいと思います。
回答:
これは私のために働きます:
return Paths.get(ClassLoader.getSystemResource(resourceName).toURI());
Thread.currentThread().getContextClassLoader().getResource(resourceName).toURI()
あなたがしたいことを推測することは、クラスパスから-おそらくjar内から-リソースからFiles.lines(...)を呼び出すことです。
Oracleは、jarファイルにある場合にgetResourceが使用可能なパスを返さないようにすることで、パスがパスであるという概念を複雑にしているため、次のようにする必要があります。
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);
}
}
}
主な障害は、2つの可能性に対処することです。たとえば、使用する必要がある既存のファイルシステムがあるが(file
URIやJava 9のモジュールストレージなど)閉じていないか、ファイルシステムを自分で開いて安全に閉じる必要があります。 zip / jarファイル)。
したがって、上記のソリューションは実際のアクションをにカプセル化し、interface
両方のケースを処理し、2番目のケースで後で安全に閉じ、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ファイルシステムプロバイダーの助けを借りて、これを実行できることがわかります。ただし、リソースURIを直接渡してPaths.get
も機能しません。代わりに、最初にjar URIのzipファイルシステムをエントリ名なしで作成し、次にそのファイルシステムのエントリを参照する必要があります。
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オブジェクトを開きますが、決して閉じないためです。最良のアプローチは、ホルガーの答えがそうするように、コンシューマーのようなワーカーオブジェクトを渡すことです。ワーカーがPathを使用して必要なことをすべて実行できるように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());
}
jarファイル内のリソースからURIを作成することはできません。これを一時ファイルに書き込んで使用するだけです(java8)。
Path path = File.createTempFile("some", "address").toPath();
Files.copy(ClassLoader.getSystemResourceAsStream("/path/to/resource"), path, StandardCopyOption.REPLACE_EXISTING);
java8で、NIOを使用してリソースフォルダーからファイルを読み取る
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();
}
https://docs.oracle.com/javase/8/docs/technotes/guides/io/fsp/zipfilesystemprovider.htmlで説明されているように、jarファイルからリソースを読み取るためのファイルシステムを定義する必要があります。私は以下のコードで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() `がありますURL
。あなたはそれらを一緒にチェーンすることができるかもしれません。まだ試していません。