使用 2 个 BufferedImage 瞬态字段序列化 Obj,不会读取第二个图像 [英] Serialize Obj with 2 BufferedImage transient fields, second image won't be read

查看:23
本文介绍了使用 2 个 BufferedImage 瞬态字段序列化 Obj,不会读取第二个图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在解释我的事情之前,我想提一下我还没有尝试搜索任何替代解决方案(我非常有信心会找到一些东西).我只是很想知道为什么会这样.

before explaining my thing i want to mention that i haven't tried-search any alternate solutions (which i am pretty confident something will be found). I am just pretty curious to know why this is happening.

所以...我有一个具有 2 个瞬态缓冲图像字段的对象(第一个图像和第二个图像,如下例所示).

So...I have an object that has 2 transient buffered images fields (firstimage and second image like the following example).

由于 bufferedimage 没有实现可序列化,一种序列化它们的方法(我在 SO 中找到)是 这个.我做了完全相同的事情,第一张图片的一切都很完美.(保存成功,加载正常)

Since bufferedimage does not implement serializable, one way to serialize them (i found in SO) is this. I did the exact same thing and everything worked perfect for the first image. (being saved successfully, being loaded fine aswell).

但是,关于第二张图片,有一个我无法解释的问题.我序列化我的对象没有任何问题,但是当我尝试加载它时,由于某种原因,第二个图像被读取为空.我的代码有什么遗漏吗?

However, about the second image there is a problem which i can't explain. I serialize my object without any problems but when i try to load it, the second image is being read as null for some reason. Is there anything i am missing to my code?

我用快速摆动应用程序展示了我的问题.

I show my problem with a quick swing application.

  1. load1 按钮将加载第一张图片并将其设置为对象.
  2. load2 按钮对第二张图片执行相同的操作
  3. 保存按钮序列化对象并保存在本地
  4. 显示按钮将显示是否有任何图像为空

所以,我启动了应用程序.单击 load1 &负载2.点击保存.单击显示 = 它们都不为空.关闭应用.

So, i start the app. Click load1 & load2. Click save. Click show = none of them is null. Close the app.

我再次启动应用程序并在启动时加载对象.我点击显示,图像 1 不为空,但图像 2 为空.

I start the app again and it loads the object on start. I click show and image1 is NOT null, but image 2 is null.

为什么会这样?我该怎么做才能使这种方式发挥作用?您建议使用哪种替代解决方案?

Why is this happening? what can i do in order to make this way work? What alternate solution would you suggest?

ps:我想避免将图像转为 base64 或 byte[] 并对其进行序列化.

p.s: I would like to avoid getting images to a base64 or to byte[] and serialize them.

Person 类(名字没有意义,我刚刚从之前的测试中得到了它):

Person class (name makes no sense i just had it from previous testing):

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.io.Serializable;

import javax.imageio.ImageIO;

public class Person implements Serializable {
    private static final long serialVersionUID = 1L;

    private transient BufferedImage firstImage;
    private transient BufferedImage secondImage;

    public Person() {
    }

    private void writeObject(ObjectOutputStream out) {
        try {
            out.defaultWriteObject();
            if (firstImage != null)
            {
                ImageIO.write(firstImage, "png", out);
                System.out.println("First image saved.");
            }
            if (secondImage != null) {
                ImageIO.write(secondImage, "png", out);
                System.out.println("Second image saved.");
            }
            System.out.println("-------------------------");
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private void readObject(ObjectInputStream in) {
        try {
            in.defaultReadObject();
            firstImage = ImageIO.read(in);
            secondImage = ImageIO.read(in);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }

    }

    public static Person loadPerson() {
        String path = "C://Users//George//Desktop//person.tmp";
        File file = new File(path);
        Person p = new Person();
        if (!file.exists())
            return p;
        try (FileInputStream fis = new FileInputStream(path); ObjectInputStream ois = new ObjectInputStream(fis);) {
            p = (Person) ois.readObject();
            ois.close();
            fis.close();
        } catch (IOException | ClassNotFoundException e1) {
            e1.printStackTrace();
        }
        return p;
    }

    public void savePerson() {
        String path = "C://Users//George//Desktop//person.tmp";
        try (FileOutputStream fos = new FileOutputStream(path); ObjectOutputStream oos = new ObjectOutputStream(fos);) {
            oos.writeObject(this);
            oos.close();
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public BufferedImage getSecondImage() {
        return secondImage;
    }

    public void setSecondImage(BufferedImage secondImage) {
        this.secondImage = secondImage;
    }

    public BufferedImage getFirstImage() {
        return firstImage;
    }

    public void setFirstImage(BufferedImage firstImage) {
        this.firstImage = firstImage;
    }
}

应用程序:

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UnsupportedLookAndFeelException;

@SuppressWarnings("serial")
public class Test extends JPanel {
    private JFrame f;
    private Person person;
    public Test() {
        JButton load1 = new JButton("load img1");
        load1.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    BufferedImage tmp = ImageIO.read(new File("C://Users//George//Desktop//pencil.png"));
                    person.setFirstImage(tmp);
                    tmp.flush();
                    tmp = null;
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        });
        add(load1);
        JButton load2 = new JButton("load img2");
        load2.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    BufferedImage tmp = ImageIO.read(new File("C://Users//George//Desktop//map.png"));
                    person.setSecondImage(tmp);
                    tmp.flush();
                    tmp = null;
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        });
        add(load2);

        JButton save = new JButton("save");
        save.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                person.savePerson();
            }
        });
        add(save);

        JButton show = new JButton("show which is null");
        show.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                if (person.getFirstImage() == null)
                    System.out.println("first image null");
                else
                    System.out.println("first image not null");
                if (person.getSecondImage() == null)
                    System.out.println("second image null");
                else
                    System.out.println("second image not null");
                System.out.println("---------------------");
            }
        });
        add(show);
    }
    private void display() {
        person = Person.loadPerson();
        f = new JFrame("Buffered images");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(this);
        f.setSize(new Dimension(300, 300));
//       f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException,
            IllegalAccessException, UnsupportedLookAndFeelException {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new Test().display();
            }
        });
    }
}

推荐答案

ImageIO.read 的文档 指出 InputStream 参数包含在 ImageInputStream 中.

The documentation for ImageIO.read states that the InputStream argument is wrapped in an ImageInputStream.

ImageInputStream 与 InputStream 不同.我认为您不能安全地假设它只读取需要的范围;它可能会为了缓存的目的而进一步阅读,或者为了知道从它的 length() 方法.

ImageInputStream is not the same as an InputStream. I don’t think you can safely assume that it only reads as far as it needs to; it may read ahead farther, for caching purposes, or perhaps in order to know what to return from its length() method.

换句话说,在读取第一个图像后,您的 InputStream 可能没有指向第二个图像开始的确切字节.

In other words, after reading the first image, your InputStream may not be pointing to the exact byte where the second image starts.

对此的一种解决方案是写入字节数组,而不是直接写入图像数据.缺点是不可扩展;如果您有非常大的图像,将它们保存在内存中将是一个性能问题.

One solution to this is to write byte arrays, rather than writing image data directly. The disadvantage is that it’s not scalable; if you have very large images, keeping them in memory will be a performance issue.

public class Person implements Serializable {

    // ...

    private void writeObject(ObjectOutputStream out)
    throws IOException {
        out.defaultWriteObject();

        if (firstImage != null) {
            ByteArrayOutputStream b = new ByteArrayOutputStream();
            ImageIO.write(firstImage, "png", b);

            out.writeInt(b.size());
            b.writeTo(out);

            System.out.println("First image saved.");
        } else {
            out.writeInt(0);
        }

        if (secondImage != null) {
            ByteArrayOutputStream b = new ByteArrayOutputStream();
            ImageIO.write(secondImage, "png", b);

            out.writeInt(b.size());
            b.writeTo(out);

            System.out.println("Second image saved.");
        } else {
            out.writeInt(0);
        }
    }

    private void readObject(ObjectInputStream in)
    throws IOException,
           ClassNotFoundException {
        in.defaultReadObject();

        int length = in.readInt();
        if (length > 0) {
            byte[] bytes = new byte[length];
            in.readFully(bytes);
            firstImage = ImageIO.read(new ByteArrayInputStream(bytes));
        }

        length = in.readInt();
        if (length > 0) {
            byte[] bytes = new byte[length];
            in.readFully(bytes);
            secondImage = ImageIO.read(new ByteArrayInputStream(bytes));
        }
    }

}

作为旁注,IOException 和 ClassNotFoundException 不应在序列化方法中被捕获.它们属于方法声明的 throws 子句.毕竟,你不希望你的类假装序列化成功了,而实际上它并没有成功.中描述了序列化方法签名的详细信息Serializable 的文档.

As a side note, IOException and ClassNotFoundException should not be caught inside the serialization methods. They belong in the throws clause of the method declaration. You don’t want your class pretending that serialization was successful when it didn’t actually succeed, after all. The details of the serialization methods’ signatures are described in the documentation for Serializable.

这篇关于使用 2 个 BufferedImage 瞬态字段序列化 Obj,不会读取第二个图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆