StepTNT
StepTNT

Reputation: 3967

Writing a BufferedImage cache and save it to disk

I'm developing a java application in which I load some long lists containing images (downloaded from web), so I added a quick HashMap<String,BufferedImage> as a cache, to avoid redownloading the same image multiple times.

This works fine and the application is way faster, but it would be nice to let this cache persist through the various sessions, so I changed my cache to be serialized.

BufferedImage is not Serializable, so I had to wrote my custom methods.

My file structure should be something like:

While file saving seems fine (at least I have no exceptions), when I try to load the URL it throws java.io.OptionalDataException with length = 4 and I don't understand why. The first iteration goes fine, but I have this exception as soon as I try to load the second URL, so I suspect that there's something wrong in the way I load the first image.

Here's the full code :

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;


public class PicturesCache {

    private static HashMap<String, BufferedImage> picturesCache;

    private static final String cacheDiskLocation = "pictures_cache.map";

    private static void writeCache(ObjectOutputStream oos, HashMap<String, BufferedImage> data) throws IOException {
        // Number of saved elements
        oos.writeInt(data.size());
        // Let's write (url, image) for each entry in the cache
        for (Entry<String, BufferedImage> entry : data.entrySet()) {   
            oos.writeObject(new URL(entry.getKey()));
            ImageIO.write(entry.getValue(), "png", oos);
        }
    }

    private static HashMap<String, BufferedImage> readCache(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        // Number of saved elements
        int size = ois.readInt();
        // Cache
        HashMap<String, BufferedImage> result = new HashMap<>(size);
        // Let's read (url, image) and add them to cache
        for (int i = 0; i < size; i++) {
            String url = ((URL) ois.readObject()).toString(); // EXCEPTION HERE
            BufferedImage image = ImageIO.read(ois);            
            result.put(url, image);
        }
        return result;
    }

    public static void loadCache() {
        picturesCache = new HashMap<>();
        File file = new File(cacheDiskLocation);
        if (file.isFile()) {
            FileInputStream fis = null;
            ObjectInputStream ois = null;
            try {
                fis = new FileInputStream(file);
                ois = new ObjectInputStream(fis);
                picturesCache = readCache(ois);
            } catch (IOException | ClassNotFoundException ex) {
                Logger.getLogger(PicturesCache.class.getName()).log(Level.SEVERE, null, ex);
            } finally {
                try {
                    ois.close();
                    fis.close();
                } catch (IOException ex) {
                    Logger.getLogger(PicturesCache.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
        System.out.println("Cache loaded with " + picturesCache.size() + " elements");
    }

    public static void saveCache() {
        File file = new File(cacheDiskLocation);
        FileOutputStream fos = null;
        ObjectOutputStream oos = null;
        try {
            if (file.isFile()) {
                file.delete();
            }
            file.createNewFile();
            fos = new FileOutputStream(file);
            oos = new ObjectOutputStream(fos);
            writeCache(oos, picturesCache);
        } catch (IOException ex) {
            Logger.getLogger(PicturesCache.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                System.out.println("Cache saved with " + picturesCache.size() + " elements");
                oos.close();
                fos.close();
            } catch (IOException ex) {
                Logger.getLogger(PicturesCache.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public static boolean contains(String url) {
        return picturesCache.containsKey(url);
    }

    public static BufferedImage get(String url) {
        return picturesCache.get(url);
    }

    public static void put(String url, BufferedImage image) {
        picturesCache.put(url, image);
    }

}

Upvotes: 1

Views: 1485

Answers (1)

Titus
Titus

Reputation: 22474

The error occurs because ImageIO.read(...) doesn't read all the data that was written using ImageIO.write(...). You can write the image to the ObjectOutputStread as a byte[]. For example:

private static void writeCache(ObjectOutputStream oos,
        HashMap<String, BufferedImage> data) throws IOException {
    // Number of saved elements
    oos.writeInt(data.size());
    // Let's write (url, image) for each entry in the cache
    for (Entry<String, BufferedImage> entry : data.entrySet()) {
        oos.writeObject(new URL(entry.getKey()));
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(entry.getValue(), "jpg", baos);
        byte[] bytes = baos.toByteArray();
        oos.writeObject(bytes);
    }
}

private static HashMap<String, BufferedImage> readCache(
        ObjectInputStream ois) throws IOException, ClassNotFoundException {
    // Number of saved elements
    int size = ois.readInt();
    // Cache
    HashMap<String, BufferedImage> result = new HashMap<>(size);
    // Let's read (url, image) and add them to cache
    for (int i = 0; i < size; i++) {
        String url = ((URL) ois.readObject()).toString(); // EXCEPTION HERE
        ByteArrayInputStream bais = new ByteArrayInputStream(
                (byte[]) ois.readObject());
        BufferedImage image = ImageIO.read(bais);
        result.put(url, image);
    }
    return result;
}

Upvotes: 1

Related Questions