本文首发于公众号:符合预期的CoyPan
背景
前段时间,收到了一个用户的需求,能否在不增加图片大小的情况下,单独增加图片体积?我当时很疑惑,和用户进行了确认,用户就是想要不改变图片的分辨率和质量,但是图片体积能从500KB,变成5MB。而之所以想要这样做,是因为:图片大,给客户交差的时候,会显得更牛逼!
分析实现
我们之前介绍过图片的相关知识,图片的大小,是由图片的像素宽度、高度,图片的位深,图片的压缩格式这几个点共同决定的。推荐阅读
用户要求不改变图片的分辨率和质量,那么图片的像素宽度、高度,以及图片的位深,就不适合做修改了。从图片的压缩格式入手。如果我们把一张png、jpeg图片,转换成bmp图片,确实图片大小会变大。但是有两个问题:1、图片格式被改变了。 2、图片变大的比例,不太可控。这不失为一种方案,还有其他办法么?
图片中存储的额外信息 exif meta data
我们在使用相机拍照时,可以记录图片的拍摄时间,地理位置等信息。这些是写入图片的额外信息。这些信息被称为:EXIF meta data ,是有规范的。使用图片查看器可以看到这些额外信息。当然,我们也可以手动、读取写入这些信息。
代码很简单,使用 piexifjs 这个包即可。
Exif meta data,是存储在图片头部的数据块,
很遗憾,可存储的数据大小是有限的,只有几十KB。
因此,我们没有办法利用exif meta data来实现我们的需求。
直接写入脏数据
既然如此,那直接往图片内直接写入无用的数据,可行么?经过尝试,答案是可行的。
操作系统在解析某个文件的时候,是逐步读取文件数据进行解析的,直到遇到EOF(end of file,文件结尾)为止。超出EOF的数据,操作系统应该就不会进行解析了。
png图片的EOF为:6082
jpg图片的EOF为:ffd9
如果往eof后面继续写入无用的数据,那么理论上操作系统就不会再进行解析了,而这些无用的数据,也会增加文件的大小。直接上核心代码:
const buffer = await blobToArrayBuffer(file);
const sourceData = new Uint8Array(buffer);
// 直接放大两倍
const targetData = new Uint8Array(sourceData.length * 2);
for (let i = 0; i < targetData.length; i += 1) {
// 脏数据全部填充为0
targetData[i] = sourceData[i] || 0;
}
const blob = new Blob([targetData], { type: getImgMimeType(getExtension(file.name)) });
return blob;
生成的图片如下,EOF后面全是填充的 0:
使用图片查看器查看:
我们可以看到,只有文件大小增加了,其余的图片信息都没有改变。想让图片变得更大,往后面塞更多的0就行了。
基本符合我们的需求了。
更进一步,我们还可以在图片数据后面插入一个视频数据,一段文本数据。让视频数据以图片为介质来传播,挺有意思的。
写在后面
起因是一个用户的需求,让我对图片有了更进一步的了解,符合预期。