クラスパスディレクトリからリソースのリストを取得する


247

メソッドのような、特定のクラスパスディレクトリからすべてのリソース名のリストを取得する方法を探していList<String> getResourceNames (String directoryName)ます。

例えば、クラスパスのディレクトリ指定したx/y/zファイルを含むa.htmlb.htmlc.htmlおよびサブディレクトリをdgetResourceNames("x/y/z")返す必要がありますList<String>以下の文字列を含みます:['a.html', 'b.html', 'c.html', 'd']

ファイルシステムとjarのリソースの両方で機能するはずです。

Files、JarFiles 、sを使用して簡単なスニペットを作成できることはわかっていますがURL、ホイールを再発明したくありません。私の質問は、既存の公的に利用可能なライブラリを考えると、実装する最も速い方法は何getResourceNamesですか?SpringおよびApache Commonsスタックはどちらも実行可能です。


1
Cfx

このプロジェクトをチェックして、リソースフォルダーのスキャンを解決します:github.com/fraballi/resources-folder-scanner
Felix Aballi

回答:


165

カスタムスキャナー

独自のスキャナーを実装します。例えば:

private List<String> getResourceFiles(String path) throws IOException {
    List<String> filenames = new ArrayList<>();

    try (
            InputStream in = getResourceAsStream(path);
            BufferedReader br = new BufferedReader(new InputStreamReader(in))) {
        String resource;

        while ((resource = br.readLine()) != null) {
            filenames.add(resource);
        }
    }

    return filenames;
}

private InputStream getResourceAsStream(String resource) {
    final InputStream in
            = getContextClassLoader().getResourceAsStream(resource);

    return in == null ? getClass().getResourceAsStream(resource) : in;
}

private ClassLoader getContextClassLoader() {
    return Thread.currentThread().getContextClassLoader();
}

Spring Framework

PathMatchingResourcePatternResolverSpring Frameworkから使用します。

ロンマモ反射

他の手法は、実行時に巨大なCLASSPATH値のために遅くなる可能性があります。より高速なソリューションは、コンパイル時に検索をプリコンパイルするronmamoのReflections APIを使用することです。


12
反射を使用している場合、すべてのあなたが実際に必要とする:new Reflections("my.package", new ResourcesScanner()).getResources(pattern)
ザップ

27
jarファイルから実行すると、最初のソリューションは機能しますか?私にとっては-そうではありません。この方法でファイルを読み取ることはできますが、ディレクトリを読み取ることができません。
Valentina Chumak

5
この回答で説明されている最初の方法-リソースとしてパスを読み取る方法は、リソースが実行可能コードと同じJARにある場合、少なくともOpenJDK 1.8では機能しないようです。失敗はかなり奇妙です-NullPointerExceptionは、JVMのファイル処理ロジックの深いところからスローされます。まるでデザイナーがこのリソースの使用を実際に予想していなかったかのようで、部分的な実装しかありません。一部のJDKまたは環境で動作する場合でも、これは動作が文書化されていないようです。
ケビン・ブーン

7
ディレクトリはファイルストリームだけなので、このようにディレクトリを読み取ることはできません。この答えは半分間違っています。
Thomas Decaux 2017年

4
最初の方法は機能しないようです-少なくとも、開発環境から実行しているときは、リソースを使い始める前に。getResourceAsStream()ディレクトリへの相対パスを使用してを呼び出すと、FileInputStream(File)が発生します。これは、ファイルがディレクトリの場合にFileNotFoundExceptionをスローするように定義されています。
アンディトーマス

52

これが
ソースコードです:forums.devx.com/showthread.php?t=153784

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

/**
 * list resources available from the classpath @ *
 */
public class ResourceList{

    /**
     * for all elements of java.class.path get a Collection of resources Pattern
     * pattern = Pattern.compile(".*"); gets all resources
     * 
     * @param pattern
     *            the pattern to match
     * @return the resources in the order they are found
     */
    public static Collection<String> getResources(
        final Pattern pattern){
        final ArrayList<String> retval = new ArrayList<String>();
        final String classPath = System.getProperty("java.class.path", ".");
        final String[] classPathElements = classPath.split(System.getProperty("path.separator"));
        for(final String element : classPathElements){
            retval.addAll(getResources(element, pattern));
        }
        return retval;
    }

    private static Collection<String> getResources(
        final String element,
        final Pattern pattern){
        final ArrayList<String> retval = new ArrayList<String>();
        final File file = new File(element);
        if(file.isDirectory()){
            retval.addAll(getResourcesFromDirectory(file, pattern));
        } else{
            retval.addAll(getResourcesFromJarFile(file, pattern));
        }
        return retval;
    }

    private static Collection<String> getResourcesFromJarFile(
        final File file,
        final Pattern pattern){
        final ArrayList<String> retval = new ArrayList<String>();
        ZipFile zf;
        try{
            zf = new ZipFile(file);
        } catch(final ZipException e){
            throw new Error(e);
        } catch(final IOException e){
            throw new Error(e);
        }
        final Enumeration e = zf.entries();
        while(e.hasMoreElements()){
            final ZipEntry ze = (ZipEntry) e.nextElement();
            final String fileName = ze.getName();
            final boolean accept = pattern.matcher(fileName).matches();
            if(accept){
                retval.add(fileName);
            }
        }
        try{
            zf.close();
        } catch(final IOException e1){
            throw new Error(e1);
        }
        return retval;
    }

    private static Collection<String> getResourcesFromDirectory(
        final File directory,
        final Pattern pattern){
        final ArrayList<String> retval = new ArrayList<String>();
        final File[] fileList = directory.listFiles();
        for(final File file : fileList){
            if(file.isDirectory()){
                retval.addAll(getResourcesFromDirectory(file, pattern));
            } else{
                try{
                    final String fileName = file.getCanonicalPath();
                    final boolean accept = pattern.matcher(fileName).matches();
                    if(accept){
                        retval.add(fileName);
                    }
                } catch(final IOException e){
                    throw new Error(e);
                }
            }
        }
        return retval;
    }

    /**
     * list the resources that match args[0]
     * 
     * @param args
     *            args[0] is the pattern to match, or list all resources if
     *            there are no args
     */
    public static void main(final String[] args){
        Pattern pattern;
        if(args.length < 1){
            pattern = Pattern.compile(".*");
        } else{
            pattern = Pattern.compile(args[0]);
        }
        final Collection<String> list = ResourceList.getResources(pattern);
        for(final String name : list){
            System.out.println(name);
        }
    }
}  

Springを使用している場合は、PathMatchingResourcePatternResolverを参照してください。


3
質問者は、標準のJavaクラスを使用してそれを実装する方法を知っていますが、既存の(Spring、Apache Commons)ライブラリをどのように利用できるかを知りたいと思っています。
Jeroen Rosenberg、

@Jeroen Rosenberg最終的に選択された別の方法もあります:)
Jigar Joshi

7
このソリューションは、Springを使用していない場合に役立ちます。この機能のみを備えているため、Springへの依存関係を追加するのはばかげています。よろしくお願いします!「ダーティ」だが便利:) PS 1つは、メソッドにFile.pathSeparatorハードコーディングする代わりに使用したい場合があります。:getResources
ティムール

5
コード例では、代わりにこれを使用するシステムに依存し: final String[] classPathElements = classPath.split(System.pathSeparator);
dcompiled

1
警告:java.class.pathシステムプロパティには、実行中の実際のクラスパスが含まれていない場合があります。あるいは、クラスローダーを調べて、クラスのロード元のURLを問い合わせることもできます。もちろん、他の注意点は、SecurityManagerでこれを実行している場合、ZipFileコンストラクターがファイルへのアクセスを拒否する可能性があるため、それをキャッチする必要があるということです。
Trejkaz、2016

24

使用する リフレクションの

クラスパス上のすべてを取得します。

Reflections reflections = new Reflections(null, new ResourcesScanner());
Set<String> resourceList = reflections.getResources(x -> true);

別の例-some.packageから拡張子.csvのすべてのファイルを取得します。

Reflections reflections = new Reflections("some.package", new ResourcesScanner());
Set<String> fileNames = reflections.getResources(Pattern.compile(".*\\.csv"));

グーグル依存関係をインポートすることなく同じことを達成する方法はありますか?このタスクを実行するためだけにこの依存関係を持たないようにします。
Enrico Giurin

1
ReflectionsはGoogleライブラリではありません。
ルークハッチソン

多分それは過去にありました。私の答えは2015年で、2015年までに「Google Reflections」というフレーズを使用してライブラリへの参照を見つけました。コードが少し移動し、code.google.comからここの githubに移動しまし
NS du Toit

@NSduToitは、おそらくGoogle Codeでコードをホストしたことによるアーティファクト(珍しい間違いではありません)であり、Googleによる作者の主張ではありません。
マットb

17

Apache commonsIOを使用する場合、ファイルシステムに使用できます(オプションで拡張フィルターを使用)。

Collection<File> files = FileUtils.listFiles(new File("directory/"), null, false);

およびリソース/クラスパスの場合:

List<String> files = IOUtils.readLines(MyClass.class.getClassLoader().getResourceAsStream("directory/"), Charsets.UTF_8);

「directoy /」がファイルシステムにあるのかリソースにあるのかわからない場合は、

if (new File("directory/").isDirectory())

または

if (MyClass.class.getClassLoader().getResource("directory/") != null)

通話の前に、両方を組み合わせて使用​​してください...


23
ファイル!=リソース
djechlin

3
確かに、リソースは常にファイルであるとは限りませんが、問題はファイル/ディレクトリに由来するリソースに関するものでした。したがって、別の場所を確認する場合は、サンプルコードを使用します。たとえば、デフォルトの構成のconfig.xmlがリソースにあり、外部のconfig.xmlが存在する場合は、代わりにそれをロードすることができるはずです...
Rob

2
そしてなぜリソースはファイルではないのですか?リソースは、zipアーカイブにアーカイブされたファイルです。それらはメモリにロードされ、特定の方法でアクセスされますが、なぜファイルではないのかわかりません。「アーカイブ内のファイル」ではないリソースの例
マイク

10
ターゲットリソースがJARファイル内にあるディレクトリ(つまり、JARにメインアプリとターゲットディレクトリが含まれている)である場合、機能しない(NullPointerException)
Carlitos Way

12

したがって、PathMatchingResourcePatternResolverの観点から、これはコードで必要なものです。

@Autowired
ResourcePatternResolver resourceResolver;

public void getResources() {
  resourceResolver.getResources("classpath:config/*.xml");
}

使用する必要があるすべてのクラスパス内のすべての可能なリソースを検索する場合は、ほんの少しの追加classpath*:config/*.xml
revau.lt

5

Spring frameworkさんはPathMatchingResourcePatternResolverこれらの事のために、本当に素晴らしいです。

private Resource[] getXMLResources() throws IOException
{
    ClassLoader classLoader = MethodHandles.lookup().getClass().getClassLoader();
    PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(classLoader);

    return resolver.getResources("classpath:x/y/z/*.xml");
}

Mavenの依存関係:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>LATEST</version>
</dependency>

5

ロブの応答の組み合わせを使用しました。

final String resourceDir = "resourceDirectory/";
List<String> files = IOUtils.readLines(Thread.currentThread().getClass().getClassLoader().getResourceAsStream(resourceDir), Charsets.UTF_8);

for(String f : files){
  String data= IOUtils.toString(Thread.currentThread().getClass().getClassLoader().getResourceAsStream(resourceDir + f));
  ....process data
}

すべての リソースを取得する方法:つまり、/' or .`で始まるか、./どれも実際に機能しなかった
javadba

3

Springを使えば簡単です。それがファイル、フォルダ、または複数のファイルであっても、可能性があります。インジェクションを介して行うことができます。

この例は、x/y/zフォルダーにある複数のファイルの挿入を示しています。

import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;

@Service
public class StackoverflowService {
    @Value("classpath:x/y/z/*")
    private Resource[] resources;

    public List<String> getResourceNames() {
        return Arrays.stream(resources)
                .map(Resource::getFilename)
                .collect(Collectors.toList());
    }
}

ファイルシステムとJARのリソースに対して機能します。


2
/ BOOT-INF / classes / static / stories以下のフォルダー名を取得したい。@Value( "classpath:static / stories / *")でコードを試してみると、JARを実行すると空のリストが返されます。クラスパス内のリソースに対しては機能しますが、JAR内にあるときは機能しません。
PS

@Value( "classpath:x / y / z / *")private Resource []リソース。私にトリックをしました。そのための検索時間があります!ありがとう!
ルドルフシュミット

3

これは機能するはずです(春がオプションでない場合):

public static List<String> getFilenamesForDirnameFromCP(String directoryName) throws URISyntaxException, UnsupportedEncodingException, IOException {
    List<String> filenames = new ArrayList<>();

    URL url = Thread.currentThread().getContextClassLoader().getResource(directoryName);
    if (url != null) {
        if (url.getProtocol().equals("file")) {
            File file = Paths.get(url.toURI()).toFile();
            if (file != null) {
                File[] files = file.listFiles();
                if (files != null) {
                    for (File filename : files) {
                        filenames.add(filename.toString());
                    }
                }
            }
        } else if (url.getProtocol().equals("jar")) {
            String dirname = directoryName + "/";
            String path = url.getPath();
            String jarPath = path.substring(5, path.indexOf("!"));
            try (JarFile jar = new JarFile(URLDecoder.decode(jarPath, StandardCharsets.UTF_8.name()))) {
                Enumeration<JarEntry> entries = jar.entries();
                while (entries.hasMoreElements()) {
                    JarEntry entry = entries.nextElement();
                    String name = entry.getName();
                    if (name.startsWith(dirname) && !dirname.equals(name)) {
                        URL resource = Thread.currentThread().getContextClassLoader().getResource(name);
                        filenames.add(resource.toString());
                    }
                }
            }
        }
    }
    return filenames;
}

これは最近反対票を受け取りました-問題を見つけた場合は、共有してください、ありがとう!
fl0w 2018

1
ありがとう@ArnoUnkrig-必要に応じて、更新されたソリューションを共有してください
fl0w 2018年

3

単体テスト中に使用した私の方法、Springなし:

URI uri = TestClass.class.getResource("/resources").toURI();
Path myPath = Paths.get(uri);
Stream<Path> walk = Files.walk(myPath, 1);
for (Iterator<Path> it = walk.iterator(); it.hasNext(); ) {
    Path filename = it.next();   
    System.out.println(filename);
}

jarを実行すると機能しません:java.nio.file.FileSystemNotFoundException at Paths.get(uri)
error1009

1

クラスパス内のすべてのリソースを一覧表示するための最も堅牢なメカニズムは、新しいJPMSモジュールシステムを含むクラスパス指定メカニズムの可能な限り幅広い配列を処理するため、現在このパターンをClassGraph使用することです。(私はClassGraphの作者です。)

List<String> resourceNames;
try (ScanResult scanResult = new ClassGraph().whitelistPaths("x/y/z").scan()) {
    resourceNames = scanResult.getAllResources().getNames();
}

0

[ Zip File System Provider ] [1]を利用してこれを実現できると思います。使用する場合FileSystems.newFileSystemと、そのZIP内のオブジェクトを「通常の」ファイルとして扱うことができます。

上記のリンクされたドキュメントでは:

に渡されるjava.util.Mapオブジェクトでzipファイルシステムの構成オプションを指定します。 FileSystems.newFileSystemメソッドに。zipファイルシステムのプロバイダー固有の構成プロパティについては、[Zipファイルシステムのプロパティ] [2]トピックを参照してください。

zipファイルシステムのインスタンスを取得したら、[ java.nio.file.FileSystem] [3]および[ java.nio.file.Path] [4]クラスのメソッドを呼び出して、ファイルのコピー、移動、名前の変更、ファイル属性の変更などの操作を実行できます。

jdk.zipfs[Java 11の状態] [5] のモジュールのドキュメント:

zipファイルシステムプロバイダーは、zipまたはJARファイルをファイルシステムとして扱い、ファイルのコンテンツを操作する機能を提供します。FileSystems.newFileSystemインストールされている場合、zipファイルシステムプロバイダーは[ ] [6] で作成できます。

これは、サンプルリソースを使用して私が作成した不自然な例です。a .zipはですが、.jar代わりにクラスパスリソースを使用するようにコードを調整できます。

セットアップ

cd /tmp
mkdir -p x/y/z
touch x/y/z/{a,b,c}.html
echo 'hello world' > x/y/z/d
zip -r example.zip x

ジャワ

import java.io.IOException;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.util.Collections;
import java.util.stream.Collectors;

public class MkobitZipRead {

  public static void main(String[] args) throws IOException {
    final URI uri = URI.create("jar:file:/tmp/example.zip");
    try (
        final FileSystem zipfs = FileSystems.newFileSystem(uri, Collections.emptyMap());
    ) {
      Files.walk(zipfs.getPath("/")).forEach(path -> System.out.println("Files in zip:" + path));
      System.out.println("-----");
      final String manifest = Files.readAllLines(
          zipfs.getPath("x", "y", "z").resolve("d")
      ).stream().collect(Collectors.joining(System.lineSeparator()));
      System.out.println(manifest);
    }
  }

}

出力

Files in zip:/
Files in zip:/x/
Files in zip:/x/y/
Files in zip:/x/y/z/
Files in zip:/x/y/z/c.html
Files in zip:/x/y/z/b.html
Files in zip:/x/y/z/a.html
Files in zip:/x/y/z/d
-----
hello world

0

リソースをリソースフォルダーに入れ、上記の回答に従っても、どちらの回答もうまくいきませんでした。トリックを作ったのは:

@Value("file:*/**/resources/**/schema/*.json")
private Resource[] resources;

-5

上記の@robの情報に基づいて、パブリックドメインにリリースする実装を作成しました。

private static List<String> getClasspathEntriesByPath(String path) throws IOException {
    InputStream is = Main.class.getClassLoader().getResourceAsStream(path);

    StringBuilder sb = new StringBuilder();
    while (is.available()>0) {
        byte[] buffer = new byte[1024];
        sb.append(new String(buffer, Charset.defaultCharset()));
    }

    return Arrays
            .asList(sb.toString().split("\n"))          // Convert StringBuilder to individual lines
            .stream()                                   // Stream the list
            .filter(line -> line.trim().length()>0)     // Filter out empty lines
            .collect(Collectors.toList());              // Collect remaining lines into a List again
}

私はgetResourcesAsStreamディレクトリでそのように動作することを期待していなかったでしょうが、それは実際に動作し、うまく動作します。

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