一个天朗气清的早晨,我开开心心的上班,突然收到一条售后问题,pad中经过我们APP保存的图片在windows上无法打开,提示“可能内存不足”,效果如下:
项目经理吓懵了,我突然觉得有点冷,晴天霹雳一般,本来白嫩柔软的包子在手上是那么的诱惑,而现在食之无味。奇怪之余赶紧搜罗一下线索。
一番查探之后发现两个临时解决方法:
1、告知用户,换个图片查看器
2、更改Windows颜色系统默认值:
1、进入控制面板 2、控制面板中将“查看方式”的“类别”改为“小图标” 3、找到“颜色管理” 4、在“颜色管理”中点击“高级”,将“设备配置文件”中的“系统默认(sRGB ...)”改为“Agfa:Swop Standard”,然后关闭退出就可以了 |
总之问题似乎跟本开发关系不大啊!包子突然又香了起来。
一顿猪拱白菜般的享受完包子的快感,回头想了一下,这么撇清关系似乎不是我这么一个有责任感的开发能干的出来的事啊。于是决定从源头查找原因。
在Android系统中图像保存用的是Bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream),一直都这么用,而且之前也将图像导出到windows系统中查看,没有问题,而现在(系统升级到了Android10)却出现了问题,对比其他几个设备发现只有Android9导出的是正常的。想到这里我决定看一下正常图像和不正常图像的区别。
文件头部前几十个差异不大,而这块正是JPEG 特征描述的关键部分,为了搞清楚具体情况,我还是搜一下JPEG的文件头信息。得到的信息如下:
二进制形式打开文件,文件开始字节为FF D8,文件结束两字节为FF D9。则初步判定文件为jpeg。 jpeg的SOI(start of image) 为FF D8,EOD(end of image)为FF D9 |
JPEG是压缩标准,JPEG/JFIF和JPEG/Exif是文件格式标准,JFIF格式,即以FF E0开始,头部含有 .. JFIF...信息,Exif以FF E1字节开始 |
看到这里,在对比两张图的信息,那么发现保存的是FF E1的也就是Exif格式,这个格式的解析如下:
字段 | 大小 | 描述 |
APP1标记 | 2Byte | FF E1 |
长度(Exif标记段的长度) | 2Byte | 除了APP1标记之外的长度 |
标识符 | 6Byte | 45 78 69 66 00 00表示“Exif”的ASCII代码,以空字节符终止 |
TIFFHeader | 8Byte | TIFF头信息,4D 4D 00 2A 00 00 00 08,其中,前2个字节: 4D 4D开头表示“MM”,遵循 Motorola 的字节序,即大端存储 49 49开头表示“II”,储遵循 intel 的字节序,即小端存储 中间2个字节: Intel 字节序,对应的存储值为 0x2a00 Motorola 的字节序,则存储值为 0x002a 最后4个字节: 表示到 IFD0(Image File Directory)的偏移,该偏移的起始位置为TIFFHeader的起始位置, |
IFD0 (Image File Directory) | 不固定 |
这一堆里,似乎也找不到有效的差异,倒是发现了一个新的线索,可以使用JPEG文件修复工具修复显示失败的文件,修复完毕之后打开正常,于是再次对比正常的文件和异常文件的差异,如下:
结果一看,这...
差距仅仅是TIFFHeader中的遵循标准的差异,其他的均不变!!!
再次复习一下前面找到的文件头信息:
4D 4D即为“MM”表示大端存储,遵循Motorola的字节序,而49 49即为“II”表示小端存储,遵循Intel 字节序,现在使用修复工具修复之后仅仅是将字节序由4D 4D改为49 49而已。而修复后的图片push到Android设备中也可以正常打开。于是我决定直接修改字节序。文件存储之后操作如下:
try {
RandomAccessFile raf = new RandomAccessFile(mFile, "rw");
byte[] buf = new byte[16];
int len = raf.read(buf);
if (len >= 13) {
if (buf[12] == 77 && buf[13] == 77) {
raf.seek(12);
raf.write(new byte[]{73, 73});
}
}
raf.close();
} catch (Exception e) {
e.printStackTrace();
}
最后图像保存,在Android设备上打开正常,导出到Windows上打开正常!