Skip to content
Advertisement

How can I output a random image when in a jar file?

The below code works when running from my editor but the image fails to load when compiled into a runnable jar file with eclipse.

    public static BufferedImage getRandomImage() {
        // returns a random image from the Images folder
        Random rand = new Random();
        URL res = Card.class.getResource("Images"); // located in /src/.../Images
        File f = new File(res.getFile());
        
        if (!f.exists()) {
            return new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
        }
        File[] files = f.listFiles();
        int random = rand.nextInt(files.length);
        BufferedImage img = null;
        try {
            img = ImageIO.read(files[random]);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return img;
    }

Could someone please suggest how I can modify my code or editor to load the files when compiled.

I have read other methods of accessing files but since I need to select randomly from a folder, I need to use the File class.

Advertisement

Answer

There is no safe way to list resources at runtime.

(Some people may suggest approaches which work sometimes, but will not work all the time. Class.getResource is not guaranteed to provide a listing; ProtectionDomain.getCodeSource can return null.)

But you don’t need to. It’s your application; you already know what files you put into it.

The best way is to either hard-code the list of files, or include a simple text file that contains a list of the files.

As an example, assume you created (or generated) a file named image-files.txt in which each line contains the base name of an image file, and embedded that file in your application:

List<String> imageNames;
try (BufferedReader linesReader = new BufferedReader(
        new InputStreamReader(
            Card.class.getResourceAsStream("image-files.txt"),
            StandardCharsets.UTF_8));
     Stream<String> lines = linesReader.lines()) {

    imageNames = lines.collect(Collectors.toList());
} catch (IOException e) {
    throw new UncheckedIOException(e);
}

int random = rand.nextInt(imageNames.length());
String imageName = imageNames.get(random)));
BufferedImage img;
try {
    img = ImageIO.read(Card.class.getResource(imageName));
} catch (IOException e) {
    throw new UncheckedIOException(e);
}

return img;

Note: The getFile() method of URL does not return a valid filename. It only returns the path portion of a URL. There are many characters which would be illegal in URLs, so the path portion percent-escapes them. If you ignore this fact, the value returned by getFile() will eventually fail.

(The reason for the misleading method name is that the URL class was part of Java 1.0, and in the mid-1990s, all URLs actually referred to physical files.)

I need to use the File class

Each .jar entry is just a subsequence of compressed bytes within a single .jar file, so you will never be able to use File to read such an entry. Class.getResource and Class.getResourceAsStream are the only correct ways to read those entries.

7 People found this is helpful
Advertisement