[關鍵字]
ImageIOSocket
Serializable
BufferedImage
NullPointerException
[問題描述]
我寫了一支 Server/Client 程式,Server 端會利用 ImageIO.write(image, "png", out) 將圖片傳給 Client 端,Client 端則用 ImageIO.read(in) 讀進為 BufferedImage 物件。但是這支程式只有第一張圖片能夠正確的傳送,從第二張開始 ImageIO.read() 就只會回傳 null,如果在你的 code 中還有對圖片進行操作,那就可能會出現 NullPointerException 或著其他的錯誤。
[原因]
經過仔細分析後,我發現 ImageIO.read() 在讀進 PNG 格式的資料時,會留下 16 byte 在 stream 內,因此在讀取第二張圖片時就會讀到這多餘的 16 byte 而造成錯誤。經由 PTT Java 板板友 LPH66 說明,這多出的 16 byte 中前 4 byte 是 PNG IDAT 區的 checksum,最後 12 byte 是 IEND 區。即使沒有這些資料,依然可以正確的解析圖片。
PTT Java 板板友 sbrhsieh 並補充這是因為 ImageReader 在處理數據的順序/數量上的不匹配造成的。在針對 PNG 格式時,ImageIO.read() 有短缺消耗的問題;而在處理 JPG 格式時,則有過度消耗的行為,也就是讀取一張圖片時,可能會連下一張圖片的資料也被消耗掉,使得下一張圖片無法正確讀入。在使用 ImageIO 與 FileInputStream 配合時,通常一個檔案內只會有一張圖片的資料,在讀進 JPG 時會遇到 EOF,因此並不會造成問題。
[解決方法]
當需要利用 ImaqeIO 與 Stream 傳送多張圖片時,我建議使用下列的方式:// 修改自 PTT Java 板板友 ogamenewbie 所提供的程式 public class SerializableImage implements Serializable { byte[] data; public SerializableImage(BufferedImage image, String type) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(image, type, baos); data = baos.toByteArray(); } public BufferedImage getBufferedImage() throws IOException { return ImageIO.read(new ByteArrayInputStream(data)); } }寫入圖片時:
SerializableImage serialImage = new SerializableImage(image, "png"); out.writeObject(serialImage);讀取圖片時:
SerializableImage serialImage = (SerializableImage)in.readObject(); BufferedImage image = serialImage.getBufferedImage();