Question

Description

Im reading Theme Objects from a file, the Theme class has a list of buffered images that is transient, but uses a custom read/write Object method that uses ImageIO's read and write. The Problem is that the first BufferedImage I read in a Theme is always ok(not null) but the rest is null, I think there might be something wrong with the writeObject method, but what?

The program goes trough folders and creates a Theme containing images that where located in that folder. There is nothing wrong with the Images, I've checked using different images, but the result is the same.


DataSetup

public class DataSetup {

    public void write() {

        List<Theme> themes = getThemes(new File("data"));

        try {

            FileOutputStream fos = new FileOutputStream("data/campaign.dat");
            ObjectOutputStream oos = new ObjectOutputStream(fos);

            for(Theme theme : themes) {

                oos.writeObject(theme);
            }

            oos.close();

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

Theme Class

package data;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;

public class Theme implements Serializable {

    private static final long serialVersionUID = 1L;

    private String name;
    private transient List<BufferedImage> bufferedImages = new ArrayList<BufferedImage();

    public Theme(String name) {

        this.name = name;
    }

    private void writeObject(ObjectOutputStream oos) throws IOException {

        oos.defaultWriteObject();
        oos.writeInt(bufferedImages.size());

        for(BufferedImage bi : bufferedImages) {

                    //Image type is JPG
            ImageIO.write(bi, Data.IMAGE_TYPE, oos);
        }
            oos.close();
    }

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {

        ois.defaultReadObject();
        final int COUNT = ois.readInt();

        bufferedImages = new ArrayList<BufferedImage>(COUNT);

        for(int i = 0; i < COUNT; i++) {

            bufferedImages.add(ImageIO.read(ois));
        }

        System.out.println("Checking for Theme " + getName());
        for(BufferedImage bi : bufferedImages) {

            System.out.println("Image is null = " + (bi==null));
        }
            ois.close();
    }

    public void addBufferedImage(BufferedImage bi) {bufferedImages.add(bi);}
    public BufferedImage getBufferedImage(int index) {return bufferedImages.get(index);}

    public String getName() {return name;}
    public int getSize() {return bufferedImages.size();}
}

Output

Checking for Theme Animal
Image is null = false
Image is null = true
Image is null = true
Image is null = true
Image is null = true
Checking for Theme Clown
Image is null = false
Image is null = true
Image is null = true
Image is null = true
Checking for Theme Mountain
Image is null = false
Image is null = true
Checking for Theme Space
Image is null = false
Image is null = true
Image is null = true
Was it helpful?

Solution

I think this is a known issue.

There's no guarantee that the ImageReader will read as many bytes as the ImageWriter originally wrote. It will read as many bytes as needed to efficiently decode (due to buffering, this might be more than the writer wrote, as the stream just continues). This might lead to the stream being "mis-aligned", and the next read(s) will fail.

The workaround is to buffer each write, then write the length (byte count) for each image, before the actual image bytes, or simply write the buffered byte arrays.

When reading back, make sure you have consumed exactly as many bytes as you wrote, by reading or skipping the necessary number of extra bytes.

To write, you can use code similar to:

BufferedImage image = null; // your image
ByteArrayOutputStream bufferStream = new ByteArrayOutputStream();
ImageIO.write(image, "JPEG", bufferStream);

byte[] bufferedBytes = bufferStream.toByteArray();

// Write bufferedBytes to ObjectOutputStream as Object, OR write bufferedBytes.length + bufferedBytes as raw bytes

To read:

byte[] bytes = ...; // from ObjectInputStream
BufferedImage image = ImageIO.read(new ByteArrayInputStream(bytes));
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top