java - 如何在jar中列出资源中的目录

时间:2018-03-29 14:45:50

标签: java spring

让我们有一个maven项目,资源包含以下结构:

的src /主/资源

  • DIR1
    • subdir1
      • 文件...
    • subdir2
      • 文件...
  • DIR2

虽然我找到了许多如何列出文件资源的答案,但我仍然坚持找到一种方法如何列出资源中目录(或路径)的所有直接子目录,当从IDE(IntelliJ)运行应用程序时这将起作用和罐子。

目标是获取目录" dir1"的子目录的名称: subdir1,subdir2

我试过

Thread.currentThread().getContextClassLoader().getResourceAsStream("dir1");

并使用返回的InputStream(包含子目录的名称),但不能从jar工作。来自IntelliJ的作品。

我也试过使用spring框架但是

PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("dir1/*");

在jar中不起作用 - resources数组为空。来自IntelliJ的作品。

我尝试修改为

Resource[] resources = resolver.getResources("dir1/*/");

它可以从jar工作,但不能从IntelliJ工作。

当我从jar工作时,我打破了另一种方式,反之亦然。我的最后一个想法是使用

Resource[] resources = resolver.getResources("dir1/**");

获取目录下的所有资源,然后只获取所需的资源。是否有更好的(最好不是hacky)方式?

4 个答案:

答案 0 :(得分:0)

jar的问题在于没有这样的文件夹,有条目,文件夹只是在其名称中带有尾部斜杠的条目。这可能是dir1/*/返回jar中文件夹而不是文件夹名称不以/结尾的de IDE的原因。由于类似的原因,dir1/*在IDE上工作但在jar中没有,因此文件夹名称以jar中的斜杠结尾。

Spring根据文件处理资源,如果你尝试将空文件夹作为资源而失败,那么这个空文件夹通常不会添加到jar中。寻找文件夹并不常见,因为内容在文件中,唯一有用的信息是它包含的文件。

实现此目的的最简单方法是使用dir1/**模式,因为这将列出所有文件以及包含dir1目录下文件的文件夹。

'检测'如果正在从jar文件执行程序,选择不同的策略列出文件夹也可以完成,但该解决方案更加“hacky”。​​

这是一个使用第一个选项的示例,模式dir1/**(仅在需要的情况下):

String folderName = "dir1";
String folderPath = String.format("%s/**", folderName);

Resource[] resources = resolver.getResources(folderPath);
Map<String, Resource> resourceByURI = Arrays.stream(resources)
        .collect(Collectors.toMap(resource -> {
            try {
                return resource.getURI().toString();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }, Function.identity()));

resourceByURI是一个包含Resource的地图,由 URI 标识。

Resource folder = resolver.getResource(folderName);
int folderLength = folder.getURI().toString().length();
Map<String, String> subdirectoryByName = resourceByURI.keySet().stream()
        .filter(name -> name.length() > folderLength
                && name.indexOf("/", folderLength + 1) == name.length() - 1)
        .collect(Collectors.toMap(Function.identity(), name -> name.substring(folderLength,
                name.indexOf("/", folderLength + 1))));

然后你可以得到文件夹的URI,计算路径的偏移量(类似于spring的做法),然后检查名称是否比当前文件夹长(以丢弃jar案例中的相同文件夹)并且该名称最终会成为一个/。最后收集到由URI标识的Map,其中包含文件夹的名称。文件夹名称的集合为subdirectoryByName.values()

答案 1 :(得分:0)

似乎有以下作品:

public String[] getResourcesNames(String path) {
    try {
        URL url = getClassLoader().getResource(path);
        if (url == null) {
            return null;
        }

        URI uri = url.toURI();
        if (uri.getScheme().equals("jar")) { // Run from jar
            try (FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap())){
                Path resourcePath = fileSystem.getPath(path);

                // Get all contents of a resource (skip resource itself), if entry is a directory remove trailing /
                List<String> resourcesNames =
                        Files.walk(resourcePath, 1)
                                .skip(1)
                                .map(p -> {
                                    String name = p.getFileName().toString();
                                    if (name.endsWith("/")) {
                                        name = name.substring(0, name.length() - 1);
                                    }
                                    return name;
                                })
                                .sorted()
                                .collect(Collectors.toList());

                return resourcesNames.toArray(new String[resourcesNames.size()]);
            }
        } else { // Run from IDE
            File resource = new File(uri);
            return resource.list();
        }
    } catch (IOException | URISyntaxException e) {
        return null;
    }
}

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

答案 2 :(得分:0)

我最终以春天告终了:

public class ResourceScanner {
    private PathMatchingResourcePatternResolver resourcePatternResolver;

    public ResourceScanner() {
        this.resourcePatternResolver = new PathMatchingResourcePatternResolver();
    }

    public Resource getResource(String path) {
        path = path.replace("\\", "/");
        return resourcePatternResolver.getResource(path);
    }

    public Resource[] getResources(String path) throws IOException {
        path = path.replace("\\", "/");
        return resourcePatternResolver.getResources(path);
    }

    public Resource[] getResourcesIn(String path) throws IOException {
        // Get root dir URI
        Resource root = getResource(path);
        String rootUri =  root.getURI().toString();

        // Search all resources under the root dir
        path = (path.endsWith("/")) ? path + "**" : path + "/**";

        // Filter only direct children
        return Arrays.stream(getResources(path)).filter(resource -> {
            try {
                String uri = resource.getURI().toString();

                boolean isChild = uri.length() > rootUri.length() && !uri.equals(rootUri + "/");
                if (isChild) {
                    boolean isDirInside = uri.indexOf("/", rootUri.length() + 1) == uri.length() - 1;
                    boolean isFileInside = uri.indexOf("/", rootUri.length() + 1) == -1;
                    return isDirInside || isFileInside;
                }

                return false;
            } catch (IOException e) {
                return false;
            }
        }).toArray(Resource[]::new);
    }

    public String[] getResourcesNamesIn(String path) throws IOException {
        // Get root dir URI
        Resource root = getResource(path);
        String rootUri = URLDecoder.decode(root.getURI().toString().endsWith("/") ? root.getURI().toString() : root.getURI().toString() + "/", "UTF-8");

        // Get direct children names
        return Arrays.stream(getResourcesIn(path)).map(resource -> {
            try {
                String uri = URLDecoder.decode(resource.getURI().toString(), "UTF-8");

                boolean isFile = uri.indexOf("/", rootUri.length()) == -1;
                if (isFile) {
                    return uri.substring(rootUri.length());
                } else {
                    return uri.substring(rootUri.length(), uri.indexOf("/", rootUri.length() + 1));
                }
            } catch (IOException e) {
                return null;
            }
        }).toArray(String[]::new);
    }
}

答案 3 :(得分:0)

感谢Lubik的第一篇文章,它在IDE或jar中的Springboot 2.1。中运行良好。

我需要从jar(或dev中的IDE)中复制和读取一些资源文件,而是返回Resource [],然后借助resource.getInputStream()将它们复制到磁盘上所需的位置:

for (Resource resource: mapResources) {
 Path destPath =
 someFolder.resolve(resource.getFilename());

 Files.copy(resource.getInputStream(), destPath);

}