查找文件系统资源(Spring Boot)的相对路径



摘要

在 Spring 中,我可以通过指定相对于我的src/main/resources/目录的路径来访问资源。例如,如果我要求/public/index.html,我会得到一个代表/Users/.../src/main/resources/public/index.htmlFileSystemResource。但是,我看不到相反方向的方法。

给定一个FileSystemResource,有没有办法找到它相对于src/main/resouces/的路径?

我正在使用 PathMatchingResourcePatternResolver 获取应用中的文件资源列表。我需要的资源位于应用的src/main/resources/public/文件夹中。

ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
for (Resource r : resolver.getResources("/public/**")) {
    // r.getURI().getString() gives me the absolute path.
}

我可以很容易地得到绝对路径,但我想要一种方法来让部分从/public/开始,因为这就是 Spring 一开始发现它的方式。

正要问如何做到这一点,以一种比我的解决方案更不黑客的方式(加上处理在我的情况下可能发生的ClassPathResource(。所以这是我的答案和问题:

ResourcePatternResolver.getResources返回的资源可以是FileSystemResourceClassPathResource,这取决于 glob 模式是否包含通配符。 (就我而言,glob 模式作为参数给出,应支持任何模式。所以,没有getFile;两者唯一可用的路径信息似乎是 getDescription .

为了确定根目录的目录深度,我在那里放置了一个参考文件"input-files.txt"。这是因为我认为根目录资源"classpath:/input-files/"在基于 jar 的类路径的情况下不需要存在。使用根深度,我可以从资源描述中删除那么多目录。

有谁知道更简单或不太黑客的解决方案?

像我的一样,它应该与 jar 和基于目录的类路径一起使用。

import java.io.IOException;
import java.nio.file.Path;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.inject.Inject;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
public class TestClasspathRelativePath implements CommandLineRunner {
    private static final String RESOURCE_ROOT = "classpath:/input-files/";
    private static final String RESOURCE_ROOT_MARKER_FILE = "input-files.txt";
    private static final String FILE_SEPARATOR_PATTERN = "[/\\]+";
    @Inject
    private ApplicationContext appContext;
    public static void main(String[] args) {
        new SpringApplicationBuilder()
                .sources(TestClasspathRelativePath.class)
                .web(false)
                .build()
                .run("**/*");
    }
    @Override
    public void run(String... args) throws Exception {
        for (String templateFilenamePattern : args) {
            for (Resource template : appContext.getResources(RESOURCE_ROOT + templateFilenamePattern)) {
                createInputFile(template);
            }
        }
    }
    private void createInputFile(Resource template) throws IOException {
        // A resource whose immediate parent is the root input
        // files directory, without wildcard lookup. (As of Spring 4.3.14, with
        // a directory classpath entry, this is a ClassPathResource.)
        Resource topLevelFile = appContext.getResource(RESOURCE_ROOT + RESOURCE_ROOT_MARKER_FILE);
        // A resource whose immediate parent is the root input
        // files directory, with wildcard lookup. (As of Spring 4.3.14, with a
        // directory classpath entry, this is a FileSystemResource.)
        Resource wildcardTopLevelFile =
                // match with min description length is
                // RESOURCE_ROOT/input-files.txt (probably sole match anyway)
                Stream.of(appContext.getResources(RESOURCE_ROOT + "**/" + RESOURCE_ROOT_MARKER_FILE)).min(
                        (r1, r2) -> Integer.valueOf(r1.getDescription().length()).compareTo(Integer.valueOf(r2.getDescription().length())))
                        .orElseThrow(() -> new IllegalStateException("resource " + RESOURCE_ROOT + "/" + RESOURCE_ROOT_MARKER_FILE + " not found"));
        // In the real program, both top level resources are computed only once and on-demand.
        String targetFilename = "<target-dir>/input/" + relativize(template, topLevelFile, wildcardTopLevelFile);
        System.out.println(targetFilename);
        // ... read template, process, write results to targetFilename
    }
    /**
     * Replacement for {@link Path#relativize(Path)} which should also work with
     * non-file resources.
     */
    private static String relativize(Resource child, Resource... topLevelFiles) {
        // find the top-level file for child's type
        Resource referenceFile = Stream.of(topLevelFiles)
                .filter(f -> f.getClass().isInstance(child))
                .findFirst()
                .orElseThrow(() -> new IllegalStateException("don't know how to relativize " + child));
        int rootLevel = descriptionUpToName(referenceFile).split(FILE_SEPARATOR_PATTERN, -1).length - 1;
        return Stream.of(descriptionUpToName(child).split(FILE_SEPARATOR_PATTERN, -1)).skip(rootLevel)
                .collect(Collectors.joining("/"));
    }
    /**
     * Hack to strip the suffix after the name from a resource description. The
     * prefix need not be stripped, because
     * {@link #relativize(Resource, Resource...)} uses a safer way to get rid of
     * that.
     *
     * @return e.g. "file [C:fooclasspathinput-filesab.txt" if the
     *         resource's description is "file
     *         [C:fooclasspathinput-filesab.txt]"
     */
    private static String descriptionUpToName(Resource resource) {
        String path = resource.getDescription();
        int i = path.lastIndexOf(resource.getFilename());
        return path.substring(0, i + resource.getFilename().length());
    }
}

相关内容

  • 没有找到相关文章