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


143

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


3
さて、長いパス(意図的にしゃれた)をとると、がありPaths.get(URI)、次にを返す´URL.toURI(), and last getResource() `がありますURL。あなたはそれらを一緒にチェーンすることができるかもしれません。まだ試していません。
NilsH 2013年

回答:


174

これは私のために働きます:

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

7
@VGR(.jarファイル内のリソースがこれを試行できる場合) `Resource resource = new ClassPathResource(" usage.txt "); BufferedReaderリーダー= new BufferedReader(new InputStreamReader(resource.getInputStream())); ` stackoverflow.com/questions/25869428/…
zhuguowei

8
@zhuguoweiはSpring固有のアプローチです。Springを使用していない場合は、まったく機能しません。
ライアンJ.マクドノウ

2
アプリケーションがシステムクラスローダに依存していない場合は、それがあるべきThread.currentThread().getContextClassLoader().getResource(resourceName).toURI()
ThrawnCA

27

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

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

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

1
前に "/"が必要かどうかはわかりませんが、私の場合class.getResourceはスラッシュが必要ですが、スラッシュがgetSystemResourceAsStream前に付いているとファイルが見つかりません。
アダム

11

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

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つの可能性に対処することです。たとえば、使用する必要がある既存のファイルシステムがあるが(fileURIやJava 9のモジュールストレージなど)閉じていないか、ファイルシステムを自分で開いて安全に閉じる必要があります。 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はのパス/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);
    }
});

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


1
このソリューションはうまくいきます!これが、ディレクトリクラスパスとjarクラスパスの両方のすべてのリソース(ファイル、ディレクトリ)で機能することを確認できます。これは間違いなく、Java 7以降で大量のリソースをコピーする方法です。
Mitchell Skaggs

10

組み込みの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を閉じます。


11
新しく作成されたfsに注意してください。同じjarを使用して2回目の呼び出しを行うと、既存のファイルシステムについて不平を言う例外がスローされます。try(FileSystem fs = ...){return fs.getPath(entryName);}を実行するか、これをキャッシュしたい場合は、より高度な処理を行うことをお勧めします。現在の形では危険です。
レイザーコスタン2013

3
閉じられていない可能性のある新しいファイルシステムの問題に加えて、スキーム間の関係に関する仮定、新しいファイルシステムを開く必要性、URIの内容に戸惑うことにより、ソリューションの有用性が制限されます。操作を簡素化し、同時に新しいJava 9クラスストレージのような新しいスキームを処理する一般的なアプローチを示す新しい回答を設定しました。それはまた...アプリケーション内の他の誰かがすでにファイルシステムを開設しました(またはメソッドが同じ瓶のために2回呼び出され)たときに動作します
ホルガー

このソリューションの使用方法によっては、閉じていないnewFileSystemため、複数のリソースが永久に開いたままになる可能性があります。@raisercostin補遺は、すでに作成されたファイルシステムを作成しようとするときのエラーを回避しPathますが、返されたを使用しようとすると、を取得しClosedFileSystemExceptionます。@Holger応答は私にとってうまく機能します。
ホセ・アンディアス2017

私は閉じませんFileSystem。Jarからリソースをロードし、次に必要なものを作成する場合FileSystem-これFileSystemにより、同じJarから他のリソースをロードすることもできます。また、新しいを作成しFileSystemたら、を使用してリソースを再度ロードするだけPaths.get(Path)で、実装は自動的に新しいを使用しFileSystemます。
NS du Toit

#getPath(String)つまり、FileSystemオブジェクトでメソッドを使用する必要はありません。
NS du Toit

5

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

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

1

jarファイル内のリソースからURIを作成することはできません。これを一時ファイルに書き込んで使用するだけです(java8)。

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

1

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

0

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)) {
            ....
        }
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.