共计 2856 个字符,预计需要花费 8 分钟才能阅读完成。
前言
“没有表情包怎么聊天?”
作为破除人类交换窘境的神器,没有什么场景是一张表情包不能表白的。想像一下,当你正同时关上 N 个 VSCode 疯狂打码的时候,DING~ 的一声脆响,产品经理发来一条音讯:昨天提的那几个 bug 修复好了吗?
基于「能发图就不打字」的准则,是时候祭出收藏夹中了大杀器了↓
简略的动图发明了进退自如的交换空间,这些动图就是咱们通常应用的 GIF 图。
业务背景
然而,在风控场景下,黑灰产利用 GIF 图片多帧的个性,把非法图片注入其中,再通过手动批改文件扩展名的形式,伪装成一般图片,无疑给危险防控减少了难度。一闪而过的非法主图,让经营小二防不胜防;图片最终定格在看似没什么问题的尾帧上,让经营小二难以捕捉到无效的要害信息。
GIF 图片尽管有“动”的性质,但在 Web 中被厚此薄彼地做为图片解决,没有提供任何非凡待遇 API,所以无法控制 GIF 图片的播放、暂停、完结监听等事件 。 那么有没有方法能让一闪而过且定格在尾帧的 GIF 图“动”起来呢?接下来咱们深刻探索,分析一下 GIF 图里都有神马。
GIF 格局介绍
图像调换格局(Graphics Interchange Format)简称 GIF,是一种位图形文件格式,以 8 位色(即 256 种颜色)重现真彩色的图像,GIF 文件外部分成许多存储块,用来存储多幅图象或者是决定图象体现行为的管制块,用以实现动画和交互式利用。GIF 具备 GIF87a 和 GIF89a 两个版本。
GIF 是一种位图。位图的大抵原理是:图片由许多的像素组成,每一个像素都被指定了一种色彩,这些像素综合起来就形成了图片。8 位的「位」即色彩深度,色彩深度由一个图像的位深决定,简略来说就是最多反对多少种颜色(举个栗子,位深为 1 的像素有两个值:黑和白。位深越大,图像可蕴含的色彩越多,色彩体现越精确,8 位 GIF 图最多蕴含 256 种颜色)
GIF87a 版本是 1987 年推出的,一个文件存储一个图像,严格不反对通明像素;GIF87a 采纳 LZW 压缩算法,它可能在放弃图像品质的前提下将图像尺寸压缩百分之二十到二十五。
GIF89a 版本是 1989 年推出的很有特色的版本,该版本 容许一个文件存储多个图像,可实现动画性能,容许某些像素通明。这个版本中,为 GIF 文档裁减了图形控制区块、备注、阐明、应用程序编程接口 4 个区块,并提供了对通明色和多帧动画的反对,如果将这些图像间断播放进去,就可能组成最简略的动画。所以常被用来存储“动静图片”,通常工夫短,体积小,内容简略,成像绝对清晰。在当初咱们所说的 GIF 一版都是 89a 的格局。
GIF 文件构造拆解
想要晓得图片是如何“动”起来的,首先理解它是如何存储的。咱们援用网络上的一张图,来看看 GIF 格局的图像文件构造:
图片起源:What’s In A GIF
GIF 格局的文件按块存储,整体上分为三局部:
- 文件头(Header)
- GIF 数据流(GIF Data Stream)
- 文件结尾(Trailer)
其中,数据流中的文本扩大块、利用扩大块和正文扩大块咱们跳过不看,让图片“动”起来的秘诀就存在于 图形控制扩大(Graphic Control Extension) 中。上面让咱们用一个栗子来一探到底吧。
样例筹备
样例图片
关上可见图片会有霎时闪动成果。
十六进制转换器
传送门
文件头
辨认一张图是不是 GIF 并不只看图片扩大格局或者图片是否会动,GIF 文件的前 6 个字节内容是 GIF 的署名和版本号,通过控制台打印咱们能够失去:
对照 ASCII 编码咱们能够失去 47 49 46 38 39 61 对应 GIF 89a
简略!持续往下看↓
GIF 数据流
图形控制扩大
咱们通过观察不难发现,图片会有霎时闪动的成果,比照文章结尾表情包图,为什么有些 GIF 图能够始终循环播放,有些却是霎时闪动而后定格在第二帧呢?
在 89a 版本,GIF 增加了图形控制扩大块,放在图像标识符(Image Descriptor)的后面,用来管制紧跟在它前面的第一个图象的显示,图形控制扩大块的构造如下图所示:
由上图可见,整个扩大块构造如下:
形容 | 长度 |
---|---|
扩大块标识符 | 1 字节、固定值 0x21 |
扩大块标识 | 1 字节、固定值 0xF9 |
扩大块子块长度 | 1 字节 |
保留位 | 3 位 |
处理办法 | 3 位 |
用户输出标记 | 1 位 |
通明色彩标记 | 1 位 |
延迟时间 | 2 字节 |
通明色彩索引 | 1 字节 |
扩大块尾 | 1 字节、固定值 0x00 |
找到它了!罪魁祸首就是 延迟时间!延迟时间标记了须要暂停这个延迟时间后再持续往下解决数据流,这里能够了解为动图中每一帧的停留时间,其单位为 1/100 秒。
剖析到这里,有种茅塞顿开的感觉,回到代码中,咱们通过控制台能够看到原图解析进去的数据是这样的:
延迟时间:00 00,十六进制转换十进制为:0
咱们通过手动设置延迟时间,就能够让一闪而过的图片“动”起来:
手动批改后的延迟时间:32 00,十六进制转换十进制为:800
外围代码如下:
let p = 0; // 以后 Buffer 解决对应的下标
while (notEndOfFile && p < contentBuffer.length) {
...
switch (contentBuffer[p++]) {
case 0xf9: // Graphics Control Extension
if (contentBuffer[p++] !== 0x4 || contentBuffer[p+4] !== 0)
throw new Error("Invalid graphics extension block.");
p++; // graphicPackedFiled
if (delay) {const delayArr = numberToByteArr(delay);
contentBuffer[p] = delayArr[delayArr.length - 1];
contentBuffer[p+1] = delayArr[delayArr.length - 2] || 0;
}
p = p + 4; // 略过 delay 2 字节, transparentIndex 1 字节,完结符号 1 字节
break;
}
}
文件结尾
当所有子图像数据解析结束,就会遇到文件尾,这一部分只有一个值为 0 的字节,标识一个 GIF 文件完结。文件尾固定为 0x3B
写在最初
在上一篇解决图片跨域的文章中笔者有介绍,借助团队 Serverless 能力搭建图片跨域转发服务器,本次的 GIF 文件解析计划是在原有的 BFF 层根底能力之上搭建的。
Octopus 图片转发服务详细信息
申请地址:https://xxx.fc.alibaba-inc.com/gifTransformer
申请办法:GET
参数:url: 必传,须要解析的图片地址
loop: 非必传,GIF 图循环次数
delay: 非必传,GIF 图每一帧播放工夫(ms)返回后果:解析后的 GIF 图
GIF 图解析最终落地危险排查业务,解决了业务始终头痛的黑灰产非法主图断定难的问题,有趣味的同学无妨上手尝试一下。
参考链接
- What’s In A GIF
作者:ES2049 | 黑眼豆豆
文章可随便转载,但请保留原文链接。
十分欢送有激情的你退出 ES2049 Studio,简历请发送至 caijun.hcj@alibaba-inc.com。