java 板


LINE

※ 引述《tkcn (小安)》之铭言: : 终於把这个问题弄清楚了。 : 让我从实验的步骤开始说明吧。 : --- : 1. 为了要方便观察接收到的封包, : 所以我做了一张只有 1x1 的图片, : 接着再用程式把这张图片读进去再重新写成图片。 : 这张图片的 16 进位 data 如下: (69-byte) : 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 : 00 00 00 01 00 00 00 01 08 02 00 00 00 90 77 53 : DE 00 00 00 0C 49 44 41 54 78 DA 63 F8 FF FF 3F : 00 05 FE 02 FE 33 12 95 14 00 00 00 00 49 45 4E : 44 AE 42 60 82 : 2. 从传送端用 ImageIO.write() 送出这张图片, : 接收端会收到完全相同的 data。 : 3. 传送端连续传送两次此图片时, : 也只是相同的 data 重复两次, : 并没有看到我所预期的...多出来的 16-byte。 : 於是我又让接收端用 ImageIO.read(), : 结果还是一样,第 1 张没问题,第 2 张就会失败。 : 4. 实验做到这里其实就可以看出端倪了, : 剩下来都只是些验证了,我就直接讲结论吧。 : ImageIO.write() 会写出 69 byte, : 而 ImageIO.read() 只会读入 53 byte, : 我试着把原始的 png 档案只留下前 53 byte,(或着是随意窜改最後 16-byte) : 这个时候秀图软体是没办法开启这个档案的, : 但是用 ImageIO.read() 还是可以正常的读入图片。 : 我猜测最後的 16-byte 应该是 png 格式的 check sum 之类的东西吧, : 而 ImageIO.read() 并没有做这样的验证,甚至资料连读都没读 Orz : 有错请指教,谢谢。 问题的症结的确是因为 png plugin 提供的 ImageReader 没有完整 consume png 数据所导致,造成传送端与接收端在处理数据的顺序/数量上的不匹配。 针对你之前的文章内容补充一些东西: ※ 引述《tkcn (小安)》之铭言: : ================================================================= : 我刚刚在测试的时候也有试过利用 ByteArrayOutputStream, : 不过没有额外包成一个 class,结果还是失败。 : 也有试过直接把 png file 的 data 丢进 ObjectOutputStream, : ( png file 当初也是用 ImageIO.write() 写入的,并且我也在接收方检查过 header ) : 结果接收方还是没办法用 ImageIO.read() 接下来。 O 版工提供给你的版本改善了不匹配的错误,其重点不在於有无包装成 Serializable 来传输,假如你改用下列这几个 method 在你的程式里,也可以正确 运作(传输/接收的程序上与 O 版工的做法相同)。 public static void serializeImage(RenderedImage image, String format, DataOutput out) throws IOException { ByteArrayOutputStream deposite = new ByteArrayOutputStream(1024*1024); ImageIO.write(image, format, deposite); deposite.close(); byte[] data = deposite.toByteArray(); System.out.printf("[serializeImage] Image => %d bytes.%n", data.length); out.writeInt(data.length); out.write(data); } public static BufferedImage deserializeImage(DataInput in) throws IOException { int frameSize = in.readInt(); System.out.printf("[deserializeImage] frame size: %d bytes.%n", frameSize); byte[] data = new byte[frameSize]; in.readFully(data); return ImageIO.read(new ByteArrayInputStream(data)); } // 假如图档格式己经是你要的就不需要先 decode 成 BufferedImage public static void deserializeImageAndSave(DataInput in, File dest) throws IOException { int frameSize = in.readInt(); System.out.printf("[deserializeImage] frame size: %d bytes.%n", frameSize); byte[] data = new byte[frameSize]; in.readFully(data); FileOutputStream out = new FileOutputStream(dest); out.write(data); out.close(); } 重点在传送/接收两方在处理数据的顺序/数量上要匹配。ObjectOutputStream 会 在 wrapped stream 加上 header,而 ObjectInputStream 建构时会消耗掉这个 header 部份,所以把 socket stream 包装成 ObjectIOStream 不会导致不匹配 得情况。有无包装成 ObjectIOStream 来使用在这个 case 里不是重点之一。 另外,不同的图档格式是由不同的 plugin 提供不同的 ImageReader/Writer,其 行为不尽相同。假如你原来的程式改成使用 jpg 格式,一样会发生接收端在读取 第二张图片时无法正确 decode 出 image,但是其原因与使用 png 格式时不同。 jpg plugin 提供的 ImageReader 在处理数据时有"过度消耗"的行为。(png ImageReader 是短缺消耗) 如果 jpg ImageReader 的 input(a ImageInputStream)只含有一个 image data, 其处理完第一个(也是最後一个) image 部份会因为遇到 EOF 而没有消耗过多的 数据。(所以上面的 deserializeImage method 没有受影响,会正确运作) 如果其 input 包含超过一个 image 的数据,ImageReader 在 decode 出第一个 image 时其已消耗的数据量已大於一个 image data。 而 ImageIO.read(...) methods 每次被调用时都是建立一个新的 ImageReader, 然後把指定的 ImageInputStream attach 到 ImageReader 来处理。这就类似你把 一个 InputStream wrap 进多个 BufferedInputStream 里,然後依序从多个 BufferedInputStream 各读取一部份的数据出来,你所得到的数据串接起来不会 等於原先 InputStream 所含的数据,因为每个 BufferedInputStream 一旦执行 过某个消耗 input 的操作後,其从 input 所消耗掉的数据量已超过可完成该操作 所需消耗的数据量。 那麽当你的程式接收端读取了第一个 image 之後,实际上 socket stream 里属於 第二个 image 的数据有一部份被消耗掉了,所以第二次调用 ImageIO.read(...) method 时,再一次建立的 ImageReader 会判读成 input 内的数据不构成一个 image。(这个 ImageReader 读到的数据是从一个 image data 的中间段开始) 对於 jpg ImageReader 的应用,可以让接收端在接收数据前先建构一个 ImageReader,整个接收程序只使用此单一 ImageReader object 来 decode 出 多个 image。至於 png ImageReader 的短缺消耗行为则无法以相同的做法来改善。 我个人觉得 JRE 内建的 png ImageReader 的此等行为应该都算是 bug 了。 ImageIO.read(InputStream) 是设计来让 client code 方便地 decode image data,不需要处理 ImageReader 与各种参数上的设定。 它的确是可以从 stream 里 decode 出 image,但是它没有在 doc 里明白 点出此操作会搞乱 stream 的状态。 ImageIO.read(InputStream) doc 里有强调 specified input stream 在此操作後 不会被 close(这是否暗示此 stream 接下来可用以读取其他的数据?),但是目前 png ImageReader 的过少消耗行为等於让 input stream 留在一个不可预期的状态 而无法再被使用。 --



※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 218.173.128.149 ※ 编辑: sbrhsieh 来自: 218.173.128.149 (01/02 15:00)
1F:推 godfat:这样设计有什麽好处..? 好怪? 01/02 18:27







like.gif 您可能会有兴趣的文章
icon.png[问题/行为] 猫晚上进房间会不会有憋尿问题
icon.pngRe: [闲聊] 选了错误的女孩成为魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一张
icon.png[心得] EMS高领长版毛衣.墨小楼MC1002
icon.png[分享] 丹龙隔热纸GE55+33+22
icon.png[问题] 清洗洗衣机
icon.png[寻物] 窗台下的空间
icon.png[闲聊] 双极の女神1 木魔爵
icon.png[售车] 新竹 1997 march 1297cc 白色 四门
icon.png[讨论] 能从照片感受到摄影者心情吗
icon.png[狂贺] 贺贺贺贺 贺!岛村卯月!总选举NO.1
icon.png[难过] 羡慕白皮肤的女生
icon.png阅读文章
icon.png[黑特]
icon.png[问题] SBK S1安装於安全帽位置
icon.png[分享] 旧woo100绝版开箱!!
icon.pngRe: [无言] 关於小包卫生纸
icon.png[开箱] E5-2683V3 RX480Strix 快睿C1 简单测试
icon.png[心得] 苍の海贼龙 地狱 执行者16PT
icon.png[售车] 1999年Virage iO 1.8EXi
icon.png[心得] 挑战33 LV10 狮子座pt solo
icon.png[闲聊] 手把手教你不被桶之新手主购教学
icon.png[分享] Civic Type R 量产版官方照无预警流出
icon.png[售车] Golf 4 2.0 银色 自排
icon.png[出售] Graco提篮汽座(有底座)2000元诚可议
icon.png[问题] 请问补牙材质掉了还能再补吗?(台中半年内
icon.png[问题] 44th 单曲 生写竟然都给重复的啊啊!
icon.png[心得] 华南红卡/icash 核卡
icon.png[问题] 拔牙矫正这样正常吗
icon.png[赠送] 老莫高业 初业 102年版
icon.png[情报] 三大行动支付 本季掀战火
icon.png[宝宝] 博客来Amos水蜡笔5/1特价五折
icon.pngRe: [心得] 新鲜人一些面试分享
icon.png[心得] 苍の海贼龙 地狱 麒麟25PT
icon.pngRe: [闲聊] (君の名は。雷慎入) 君名二创漫画翻译
icon.pngRe: [闲聊] OGN中场影片:失踪人口局 (英文字幕)
icon.png[问题] 台湾大哥大4G讯号差
icon.png[出售] [全国]全新千寻侘草LED灯, 水草

请输入看板名称,例如:Boy-Girl站内搜寻

TOP