从classpath目录获取资源列表


247

我正在寻找一种从给定的classpath目录中获取所有资源名称的列表的方法,例如method 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和URLs 编写一个简短的代码段,但是我不想重新发明轮子。我的问题是,鉴于现有的公共可用库,最快的实现方法是getResourceNames什么?Spring和Apache Commons堆栈都是可行的。



检查该项目,解决资源文件夹扫描问题:github.com/fraballi/resources-folder-scanner
Felix Aballi

Answers:


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

春季框架

PathMatchingResourcePatternResolver从Spring Framework 使用。

龙马莫思考

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


12
如果使用Reflections,则您实际需要的是:new Reflections("my.package", new ResourcesScanner()).getResources(pattern)
zapp 2013年

27
从jar文件执行时,第一个解决方案有效吗?对我来说,事实并非如此。我可以通过这种方式读取文件,但无法读取目录。
Valentina Chumak '16

5
此答案中描述的第一种方法-将路径读取为资源,但至少在OpenJDK 1.8中,当资源与可执行代码位于同一JAR中时,它似乎不起作用。失败很奇怪-从JVM的文件处理逻辑的深处抛出了NullPointerException。好像设计人员并没有真正预料到这种资源的使用,只有部分实现。即使它可以在某些JDK或环境上运行,也似乎没有记录这种行为。
Kevin Boone

7
您不能像这样读取目录,因为除了文件流之外没有目录。这个答案是错误的一半。
Thomas Decaux

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
Questioner知道如何使用标准的Java类来实现它,但是他想知道如何利用现有的(Spring,Apache Commons)库。
耶隆·罗森伯格

@杰罗恩·罗森伯格(Jeroen Rosenberg)还有另外一种最终被选择的方法:)
吉加尔·乔希

7
我发现此解决方案对您不使用Spring的情况很有用-仅由于此功能而对Spring添加依赖项是荒谬的。所以谢谢!“脏”但有用:) PS一个人可能想使用File.pathSeparator而不是:getResources方法中进行硬编码。
帖木儿

5
代码示例取决于系统,请改用此代码: final String[] classPathElements = classPath.split(System.pathSeparator);
dcompiled 2012年

1
注意:java.class.path系统属性可能不包含您正在其下运行的实际类路径。您也可以查看类加载器,并查询它从中加载类的URL。当然,另一个警告是,如果您在SecurityManager下执行此操作,则ZipFile构造函数可以拒绝您对该文件的访问,因此您应该抓住这一点。
Trejkaz

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"));

有什么方法可以实现相同而无需导入google依赖项?我想避免仅具有依赖关系来执行此任务。
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 2013年

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 frameworkPathMatchingResourcePatternResolver是这些东西真的真棒:

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

结合了Rob的回应。

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 / *”)私有Resource []资源;向我做了把戏。有搜索时间!谢谢!
鲁道夫·施密特

3

这应该可以工作(如果无法选择spring):

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

1
谢谢@ArnoUnkrig-如果您愿意,请分享更新的解决方案
fl0w

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时不起作用:Paths.get(uri)上的java.nio.file.FileSystemNotFoundException
error1009


0

我认为您可以利用[ Zip文件系统提供程序 ] [1]来实现这一目标。使用FileSystems.newFileSystem它时,您可以将该ZIP中的对象视为“常规”文件。

在上面的链接文档中:

在传递给FileSystems.newFileSystem方法的java.util.Map对象中指定zip文件系统的配置选项。有关zip文件系统的提供程序特定的配置属性的信息,请参见[Zip文件系统属性] [2]主题。

有了zip文件系统的实例后,就可以调用[ java.nio.file.FileSystem] [3]和[ java.nio.file.Path] [4]类的方法来执行诸如复制,移动和重命名文件以及修改文件属性之类的操作。

jdk.zipfs[Java 11状态] [5]中的模块文档:

zip文件系统提供程序将zip或JAR文件视为文件系统,并提供了处理文件内容的功能。FileSystems.newFileSystem如果已安装,则可以通过[ ] [6] 创建zip文件系统提供程序。

这是我使用您的示例资源做的人为示例。请注意,a .zip是a .jar,但您可以修改代码以改用classpath资源:

建立

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在目录上那样工作,但确实可以,而且效果很好。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.