【问题标题】:Get a List of Files from a Classpath Resource Folder?从类路径资源文件夹中获取文件列表?
【发布时间】:2022-02-05 05:12:01
【问题描述】:

我正在尝试从资源文件夹设置 JFX ImageView 图像,但似乎无法获得不会引发异常的适当 URL/字符串文件路径。

var x = getRandomImageFromPackage("pictures").toString();
var y = getClass().getClassLoader().getResource("pictures/mindwave/active/Super Saiyan.gif").toString();
this.iconImageView.setImage(new Image(x));

x 返回

/home/sarah/Desktop/Dropbox/School/Current/MAX MSP Utilities/MindWaveMobileDataServer/target/classes/pictures/0515e3b7cb30ac92ebfe729440870a5c.jpg

y 返回的内容类似于:

file:/home/sarah/Desktop/Dropbox/School/Current/MAX%20MSP%20Utilities/MindWaveMobileDataServer/target/classes/pictures/mindwave/active/Super%20Saiyan.gif

理论上,这两种方法中的任何一种都是可以接受的,但是,只有将 x 放在下面的 setImage(String) 行中时才会抛出异常。

有什么方法可以获取包中的图像列表,以便我可以随机选择一个并设置 ImageView?

我知道有一个自定义扫描仪选项,但它看起来相当过时(超过 11 岁,当时并不真正支持):

例行公事:

/**
 * Gets a picture from the classpath pictures folder.
 *
 * @param packageName The string path (in package format) to the classpath
 * folder
 * @return The random picture
 */
private Path getRandomImageFromPackage(String packageName) {
    try {
        var list = Arrays.asList(new File(Thread.currentThread().getContextClassLoader().getResource(packageName)
                .toURI()).listFiles());
        var x = list.get(new Random().nextInt(list.size())).toString();
        return list.get(new Random().nextInt(list.size())).toPath();

    } catch (URISyntaxException ex) {
        throw new IllegalStateException("Encountered an error while trying to get a picture from the classpath"
                + "filesystem", ex);
    }
}

供参考,这是资源文件夹:

【问题讨论】:

  • 查看this answer 了解列出资源(或执行一般文件系统查询)的最新方法。它至少适用于默认文件系统,以及 jar 文件和模块映像。如果他们的作者足够关心添加文件系统实现,其他部署的表单也可以工作。

标签: java javafx classpath java-17


【解决方案1】:

没有简单可靠的方法可以做到这一点。因此,我创建了一个清单文件并将其放入我的资源文件夹中。所以在运行时我可以读入它,然后获得我需要的所有文件名。

这是一个小测试,展示了我如何创建该文件:

public class ListAppDefaultsInventory {

    @Test
    public void test() throws IOException {
        List<String> inventory = listFilteredFiles("src/main/resources/app-defaults", Integer.MAX_VALUE);
        assertFalse("Directory 'app-defaults' is empty.", inventory.isEmpty());
        System.out.println("# src/main/resources/app-defaults-inventory.txt");
        inventory.forEach(s -> System.out.println(s));
    }
    
    public List<String> listFilteredFiles(String dir, int depth) throws IOException {
        try (Stream<Path> stream = Files.walk(Paths.get(dir), depth)) {
            return stream
                .filter(file -> !Files.isDirectory(file))
                .filter(file -> !file.getFileName().toString().startsWith("."))
                .map(Path::toString)
                .map(s -> s.replaceFirst("src/main/resources/app-defaults/", ""))
                .collect(Collectors.toList());
        }
    }
    
}

【讨论】:

    【解决方案2】:

    您的方法存在问题

    您没有格式正确的网址

    new Image(String url) 将 url 作为参数。

    空格不是 URL 的有效字符:

    这就是为什么您的 x 字符串不是有效的 URL 并且不能用于构建图像的原因。

    您需要提供 Image 构造函数识别的输入

    请注意,它稍微复杂一些,因为从 Image javadoc 开始,url 参数可以不是直接的 url,但即便如此,它们都不匹配您要查找的内容。

    如果将 URL 字符串传递给构造函数,它可以是 以下:

    1. 可以由上下文 ClassLoader 解析的资源的名称 对于这个线程
    2. 可以通过File解析的文件路径
    3. 一个 URL 可以通过 URL 解析并且存在协议处理程序

    RFC 2397 除协议外,还支持 URL 的“数据”方案 为应用程序注册的处理程序。如果 URL 使用 “数据”方案,数据必须是 base64 编码并且 MIME 类型必须 要么是空的,要么是图像类型的子类型。

    您假设资源位于文件系统中,但这并不总是有效

    如果您将资源打包到 jar 中,那么这将不起作用:

    Arrays.asList(
        new File(
            Thread.currentThread()
                .getContextClassLoader()
                .getResource(packageName)
                .toURI()
        ).listFiles()
    );
    

    这不起作用,因为 jar 中的文件是使用jar: 协议而不是file: 协议定位的。因此,您将无法从 getResource 返回的 jar: 协议 URI 创建 File 对象。

    推荐方法:使用 Spring

    从 jar 中获取资源列表实际上是一件非常棘手的事情。从您链接的问题中,最简单的解决方案是使用

    不幸的是,这意味着需要对 Spring 框架的依赖才能使用它,这对于这个任务来说完全是矫枉过正。 . .但是我不知道任何其他简单的强大解决方案。但至少你可以只调用 Spring 实用程序类,你不需要启动一个完整的 spring 依赖注入容器来使用它,所以你根本不需要知道任何 Spring 或承受任何 Spring 开销来做就这样吧。

    所以,你可以这样写(ResourceLister 是我创建的一个类,以及 toURL 方法,请参见示例应用程序):

    public List<String> getResourceUrls(String locationPattern) throws IOException {
        ClassLoader classLoader = ResourceLister.class.getClassLoader();
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(classLoader);
    
        Resource[] resources = resolver.getResources(locationPattern);
    
        return Arrays.stream(resources)
                .map(this::toURL)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }
    

    可执行示例

    ResourceLister.java

    import org.springframework.core.io.Resource;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    
    import java.io.IOException;
    import java.util.*;
    import java.util.stream.Collectors;
    
    public class ResourceLister {
        // currently, only gets pngs, if needed, can add
        // other patterns and union the results to get 
        // multiple image types. 
        private static final String IMAGE_PATTERN =
                "classpath:/img/*.png";
    
        public List<String> getImageUrls() throws IOException {
            return getResourceUrls(IMAGE_PATTERN);
        }
    
        public List<String> getResourceUrls(String locationPattern) throws IOException {
            ClassLoader classLoader = ResourceLister.class.getClassLoader();
            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(classLoader);
    
            Resource[] resources = resolver.getResources(locationPattern);
    
            return Arrays.stream(resources)
                    .map(this::toURL)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
        }
    
        private String toURL(Resource r) {
            try {
                if (r == null) {
                    return null;
                }
    
                return r.getURL().toExternalForm();
            } catch (IOException e) {
                return null;
            }
        }
    
        public static void main(String[] args) throws IOException {
            ResourceLister lister = new ResourceLister();
            System.out.println(lister.getImageUrls());
        }
    }
    

    AnimalApp.java

    import javafx.application.Application;
    import javafx.geometry.*;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.image.*;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    
    import java.io.IOException;
    import java.util.*;
    import java.util.stream.Collectors;
    
    public class AnimalApp extends Application {
        private static final double ANIMAL_SIZE = 512;
    
        // remove the magic seed if you want a different random sequence all the time.
        private final Random random = new Random(42);
    
        private final ResourceLister resourceLister = new ResourceLister();
    
        private List<Image> images;
    
        @Override
        public void init() {
            List<String> imageUrls = findImageUrls();
            images = imageUrls.stream()
                    .map(Image::new)
                    .collect(Collectors.toList());
        }
    
        @Override
        public void start(Stage stage) {
            ImageView animalView = new ImageView();
            animalView.setFitWidth(ANIMAL_SIZE);
            animalView.setFitHeight(ANIMAL_SIZE);
            animalView.setPreserveRatio(true);
    
            Button findAnimalButton = new Button("Find animal");
            findAnimalButton.setOnAction(e ->
                    animalView.setImage(randomImage())
            );
    
            VBox layout = new VBox(10,
                    findAnimalButton,
                    animalView
            );
            layout.setPadding(new Insets(10));
            layout.setAlignment(Pos.CENTER);
    
            stage.setScene(new Scene(layout));
            stage.show();
        }
    
        private List<String> findImageUrls() {
            try {
                return resourceLister.getImageUrls();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            return new ArrayList<>();
        }
    
        /**
         * Chooses a random image.
         *
         * Allows the next random image chosen to be the same as the previous image.
         *
         * @return a random image or null if no images were found.
         */
        private Image randomImage() {
            if (images == null || images.isEmpty()) {
                return null;
            }
    
            return images.get(random.nextInt(images.size()));
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.example</groupId>
        <artifactId>resource-lister</artifactId>
        <version>1.0-SNAPSHOT</version>
        <name>resource-lister</name>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <junit.version>5.7.1</junit.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-controls</artifactId>
                <version>17.0.2</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>LATEST</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.1</version>
                    <configuration>
                        <source>17</source>
                        <target>17</target>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    

    图片

    放在 src/main/resources/img.

    • 鸡.png
    • cow.png
    • 猪.png
    • sheep.png

    执行命令

    为您的 JavaFX SDK 安装设置 VM 参数:

    -p C:\dev\javafx-sdk-17.0.2\lib --add-modules javafx.controls
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-07-12
      • 2010-12-02
      • 2016-12-25
      • 1970-01-01
      • 2021-12-21
      • 1970-01-01
      • 2023-03-27
      • 2020-10-31
      相关资源
      最近更新 更多