web-dev-qa-db-ja.com

クラスパスリソースのjava.nio.file.Path

クラスパスリソース(たとえば、 Class.getResource(String) から取得するもの)をJava.nio.file.Pathとして取得するAPIはありますか?理想的には、クラスパスリソースを備えた派手な新しいPath APIを使用したいと思います。

122
Louis Wasserman

これは私のために働く:

return Paths.get(ClassLoader.getSystemResource(resourceName).toURI());
133
keyoxy

あなたがしたいことを推測すると、クラスパスから来たリソースでFiles.lines(...)を呼び出すことです-おそらくjar内から。

Oracleは、jarにある場合にgetResourceが使用可能なパスを返さないようにすることで、PathがPathであるという概念を複雑にしたため、次のようにする必要があります。

Stream<String> stream = new BufferedReader(new InputStreamReader(ClassLoader.getSystemResourceAsStream("/filename.txt"))).lines();
24
user2163960

組み込みの Zip File Systemプロバイダー を使用して、これを実行できることがわかりました。ただし、リソース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 FileSystemを開き(ワーカーが後で使用するためにPathオブジェクトを保存しない限り)、FileSystemを閉じます。

9
VGR

最も一般的な解決策は次のとおりです。

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のモジュールストレージのように)使用する必要がある既存のファイルシステムを使用するか、自分でファイルシステムを開いて安全に閉じる必要があるという2つの可能性に対処することですZip/jarファイルなど)。

したがって、上記のソリューションは、実際のアクションをinterfaceにカプセル化し、両方のケースを処理し、2番目のケースで安全に閉じ、Java 7からJava 10で動作します。新しいファイルシステムを開く前に既に開いているファイルシステムがあるかどうかを調べます。また、アプリケーションの別のコンポーネントが同じZip/jarファイルのファイルシステムをすでに開いている場合にも機能します。

上記のすべてのJavaバージョンで使用できます。パッケージの内容(例ではJava.lang)をPathsとしてリストするには、次のようにします。

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はObject.class.getResource("Object.class")のパス/Java.base/Java/lang/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);
    }
});

その後、すべてのバージョンと保存方法で再び動作します。

9
Holger

クラスリソースからPathsを読み取る小さなヘルパーメソッドを作成しました。リソース自体の名前だけでなく、リソースを保存したクラスの参照のみを必要とするため、使用すると非常に便利です。

public static Path getResourcePath(Class<?> resourceClass, String resourceName) throws URISyntaxException {
    URL url = resourceClass.getResource(resourceName);
    return Paths.get(url.toURI());
}  
4

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();
    }
1
Real Coder

Jarファイル内のリソースからURIを作成することはできません。単純に一時ファイルに書き込んでから使用できます(Java8):

Path path = File.createTempFile("some", "address").toPath();
Files.copy(ClassLoader.getSystemResourceAsStream("/path/to/resource"), path, StandardCopyOption.REPLACE_EXISTING);
1
user1079877

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)) {
            ....
        }
    }
0
user3050291