我正在寻找一种方法来从给定的类路径目录中获得所有资源名称的列表,类似于方法list <String> getResourceNames (String directoryName)。

例如,给定一个类路径目录x/y/z,其中包含文件a.html, b.html, c.html和子目录d, getResourceNames("x/y/z")应该返回一个包含以下字符串的List<String>:['a.html', 'b.html', 'c.html', 'd']。

它应该同时适用于文件系统和jar中的资源。

我知道我可以用Files, JarFiles和url编写一个快速的片段,但我不想重新发明轮子。我的问题是,给定现有的公共可用库,实现getResourceNames的最快方法是什么?Spring和Apache Commons栈都是可行的。


当前回答

扩展Luke hutchinson上面的回答,使用他的ClassGraph库,我几乎不费什么力气就能轻松地获得Resource文件夹中所有文件的列表。

假设在资源文件夹中,有一个名为MyImages的文件夹。下面是获取该文件夹中所有文件的URL列表的简单方法:

import io.github.classgraph.ClassGraph;
import io.github.classgraph.ResourceList;
import io.github.classgraph.ScanResult;

public static LinkedList<URL> getURLList (String folder) {
    LinkedList<URL> urlList    = new LinkedList<>();
    ScanResult      scanResult = new ClassGraph().enableAllInfo().scan();
    ResourceList    resources  = scanResult.getAllResources();
    for (URL url : resources.getURLs()) {
        if (url.toString().contains(folder)) {
            urlList.addLast(url);
        }
    }
    return urlList;
}

然后你只需这样做:

LinkedList<URL> myURLFileList = getURLList("MyImages");

这些url可以被加载到流中,或者使用Apache的FileUtils将文件复制到其他地方,就像这样:

String outPath = "/My/Output/Path";
for(URL url : myURLFileList) {
    FileUtils.copyURLToFile(url, new File(outPath, url.getFile()));
}

我认为ClassGraph是一个非常灵巧的库,可以使这样的任务非常简单和容易理解。

其他回答

所以在PathMatchingResourcePatternResolver方面,这是代码中需要的:

@Autowired
ResourcePatternResolver resourceResolver;

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

基于上面@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在目录上像那样工作,但它确实做到了,而且工作得很好。

使用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中的资源。

结合了罗伯的回答。

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
}

自定义扫描

实现您自己的扫描器。例如:

(评论中提到了此解决方案的局限性)

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框架

使用Spring框架中的PathMatchingResourcePatternResolver。

Ronmamo反射

对于巨大的CLASSPATH值,其他技术在运行时可能会很慢。一个更快的解决方案是使用ronmamo的Reflections API,它在编译时预编译搜索。