'Get a List of Files from a Classpath Resource Folder?
I'm trying to set an JFX ImageView image from a resource folder, but can't seem to get an appropriate URL/String filepath that won't throw an exception.
var x = getRandomImageFromPackage("pictures").toString();
var y = getClass().getClassLoader().getResource("pictures/mindwave/active/Super Saiyan.gif").toString();
this.iconImageView.setImage(new Image(x));
x returns
/home/sarah/Desktop/Dropbox/School/Current/MAX MSP Utilities/MindWaveMobileDataServer/target/classes/pictures/0515e3b7cb30ac92ebfe729440870a5c.jpg
whereas y returns something that looks like:
file:/home/sarah/Desktop/Dropbox/School/Current/MAX%20MSP%20Utilities/MindWaveMobileDataServer/target/classes/pictures/mindwave/active/Super%20Saiyan.gif
In theory either of these would be acceptable, however, only x will throw an exception if it is placed in the below setImage(String) line.
Is there any way to get a list of images in the package so that I can select a random one and set the ImageView?
I know that there was a custom scanner option, but it appears rather dated (being over 11 years old and wasn't really supported at the time):
Routine:
/**
* 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);
}
}
For reference, this is the resource folder:
Solution 1:[1]
There is no easy and reliable way to do that. Therefore I create and put an inventory file into my resources folder. So at runtime I can read that in and then have all the file names awailable that I need.
Here is a little test that shows how I create that file:
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());
}
}
}
Solution 2:[2]
Issues with your approach
You don't have a well-formed url
new Image(String url) takes a url as a parameter.
A space is not a valid character for a URL:
which is why your x string is not a valid URL and cannot be used to construct an image.
You need to provide an input recognized by the Image constructor
Note, that it is slightly more complex because, from the Image javadoc, the url parameter can be somethings other than a straight url, but even still, none of them match what you are trying to lookup.
If a URL string is passed to a constructor, it be any of the following:
- the name of a resource that can be resolved by the context ClassLoader for this thread
- a file path that can be resolved by File
- a URL that can be resolved by URL and for which a protocol handler exists
The RFC 2397 "data" scheme for URLs is supported in addition to the protocol handlers that are registered for the application. If a URL uses the "data" scheme, the data must be base64-encoded and the MIME type must either be empty or a subtype of the image type.
You are assuming the resources are in a file system, but that won't always work
If you pack your resources into a jar, then this will not work:
Arrays.asList(
new File(
Thread.currentThread()
.getContextClassLoader()
.getResource(packageName)
.toURI()
).listFiles()
);
This doesn't work because files in the jar are located using the jar: protocol rather than the file: protocol. So, you will be unable to create File objects from the jar: protocol URIs that will be returned by getResource.
Recommended Approach: Use Spring
Getting a list of resources from a jar is actually a pretty tricky thing. From the question you linked, the easiest solution is the one which uses
Unfortunately, that means requiring a dependency on the Spring framework to use it, which is total overkill for this task . . . however I don't know of any other simple robust solution. But at least you can just call the Spring utility class, you don't need to start up a whole spring dependency injection container to use it, so you don't really need to know any Spring at all or suffer any Spring overhead to do it this way.
So, you could write something like this (ResourceLister is a class I created, as well as the toURL method, see the example app):
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());
}
Executable Example
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>
images
Place in src/main/resources/img.
- chicken.png
- cow.png
- pig.png
- sheep.png
execution command
Set the VM arguments for your JavaFX SDK installation:
-p C:\dev\javafx-sdk-17.0.2\lib --add-modules javafx.controls
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | mipa |
| Solution 2 |





