反向优化?如何在不增加图片分辨率的情况下,单纯增加图片体积

137 阅读3分钟

本文首发于公众号:符合预期的CoyPan

背景

前段时间,收到了一个用户的需求,能否在不增加图片大小的情况下,单独增加图片体积?我当时很疑惑,和用户进行了确认,用户就是想要不改变图片的分辨率和质量,但是图片体积能从500KB,变成5MB。而之所以想要这样做,是因为:图片大,给客户交差的时候,会显得更牛逼!

分析实现

我们之前介绍过图片的相关知识,图片的大小,是由图片的像素宽度、高度,图片的位深,图片的压缩格式这几个点共同决定的。推荐阅读

用户要求不改变图片的分辨率和质量,那么图片的像素宽度、高度,以及图片的位深,就不适合做修改了。从图片的压缩格式入手。如果我们把一张png、jpeg图片,转换成bmp图片,确实图片大小会变大。但是有两个问题:1、图片格式被改变了。 2、图片变大的比例,不太可控。这不失为一种方案,还有其他办法么?

图片中存储的额外信息 exif meta data

我们在使用相机拍照时,可以记录图片的拍摄时间,地理位置等信息。这些是写入图片的额外信息。这些信息被称为:EXIF meta data ,是有规范的。使用图片查看器可以看到这些额外信息。当然,我们也可以手动、读取写入这些信息。

image-20230225163415165.png

代码很简单,使用 piexifjs 这个包即可。

Exif meta data,是存储在图片头部的数据块,

image-20230225163743172.png

很遗憾,可存储的数据大小是有限的,只有几十KB。

image-20230225163909468.png

因此,我们没有办法利用exif meta data来实现我们的需求。

直接写入脏数据

既然如此,那直接往图片内直接写入无用的数据,可行么?经过尝试,答案是可行的。

操作系统在解析某个文件的时候,是逐步读取文件数据进行解析的,直到遇到EOF(end of file,文件结尾)为止。超出EOF的数据,操作系统应该就不会进行解析了。

png图片的EOF为:6082

image-20230225164429853.png

jpg图片的EOF为:ffd9

image-20230225164521415.png

如果往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:

image-20230225165632928.png

使用图片查看器查看:

image-20230225170218067.png

我们可以看到,只有文件大小增加了,其余的图片信息都没有改变。想让图片变得更大,往后面塞更多的0就行了。

基本符合我们的需求了。

更进一步,我们还可以在图片数据后面插入一个视频数据,一段文本数据。让视频数据以图片为介质来传播,挺有意思的。

写在后面

起因是一个用户的需求,让我对图片有了更进一步的了解,符合预期。