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

查看:81
本文介绍了使用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个瞬态缓冲图像字段(firstimage和second image如下所示)

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).

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

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. 显示按钮将显示是否有任何图像为空

  1. load1 button will load the first image and set it to the object.
  2. load2 button does the same thing for the second image
  3. Save button serializes the object and saves it locally
  4. Show button will show if any image is null

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

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

我再次启动应用程序,并在启动时加载了对象。我单击显示,并且image1不为空,但是图像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 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 子句。毕竟,您不希望您的班级假装序列化实际上没有成功就成功了。 中描述了序列化方法签名的详细信息。可序列化的文档

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天全站免登陆