一、前言
上周五公司内部的Any Topic Conf
.上我和同事们分享了这个主题,有同事说这个有用,有同事说这个没啥用,后来还延伸到网站性能的话题上,大家讨论的激烈程度让我觉得这次选题还不错。本篇先不管到底有用与否,仅仅记录理论知识。也希望大家一起来分享实战经验啊!
二、从HTTP URI Scheme
入手
对于 <a href="http://github.com">HTTP URI Scheme</a>
我想大家都应该很熟悉了,href
属性值http://github.com
就是HTTP URI Scheme
,那么什么是DATA URI Scheme
呢?其实就是形如data:text/jpeg;base64,XINGSXXIANGJIJIGSAG==
的资源链接,一般出现在img
元素的src
属性。
DATA URI Scheme
的作用,一般就是将经过Base64
编码的数据嵌入网页中,从而减少请求资源的链接数。上面的DATA URI Scheme
中 base64
, 后的字符就是经过base64
编码后的数据,浏览器会对其解码并渲染该图片资源。
三、Data URI Scheme
格式
data:①[<mime type>]②[;charset=<charset>]③[;<encoding>]④,<encoded data>⑤
①. data
:协议名称;
②. [<mime type>]
:可选项,数据类型(image/png、text/plain
等)
③. [;charset=<charset>]
:可选项,源文本的字符集编码方式
④. [;<encoding>]
:数据编码方式(默认US-ASCII,BASE64
两种)
⑤. ,<encoded data>
:编码后的数据
注意:
[a]. [<mime type>][;charset=<charset>]
的缺省值为HTTP Header
中Content-Type
的字段值;
[b]. [;<encoding>]
的默认值为US-ASCII
,就是每个字符会编码为%xx
的形式;
[c]. [;charset=<charset>]
对于IE
是无效的,需要通过 charset
设置编码方式;而Chrome
则是 charset
属性设置编码无效,要通过 [;charset=<charset>]
来设置;FF
就两种方式均可。
[d]. 若 ,<encoded data>
不是以 [;<encoding>]
方式编码后的数据,则会报异常
四、示例
/**
* data:,文本数据
* data:text/plain,文本数据
* data:text/html,HTML代码
* data:text/css;base64,css代码
* data:text/javascript;base64,javascript代码
* data:image/x-icon;base64,base64编码的icon图片数据
* data:image/gif;base64,base64编码的gif图片数据
* data:image/png;base64,base64编码的png图片数据
* https://img-blog.csdnimg.cn/2022010612382527833.jpegbase64编码的jpeg图片数据,示例:
*/
body { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAGElEQVQIW2P4DwcMDAxAfBvMAhEQMYgcACEHG8ELxtbPAAAAAElFTkSuQmCC");}
/**
* data:text/css,css代码,示例:
* 注意:下列方式是无法设置background-image:url()样式的
*/
<link rel="stylesheet" type="text/css" href="data:text/css;charset=gbk,#pseudo{color:red;}"/>
//data:text/javascript,javascript代码,示例:
<script type="text/javascript" charset="gbk" src="data:text/javascript;charset=gbk,alert('%D6%D0%CE%C4')"></script>
五、优点&缺点
优点:
①. 减少资源请求链接数。
缺点:
①. 不会被浏览器缓存起来;
②. 移动端性能比http URI scheme
低。
六、优化方案
通过在css
文件的background-image
样式规则使用Data URI Scheme
,使其随css
文件一同被浏览器缓存起来。
七、浏览器支持
①. 支持
Opera 7.2+ data URI
必须小于4100字符
IE8+ data URI
必须小于32k(IE8不支持js的data URI)
Chrome、FF和Safari无限制
②. 不支持
IE567
八、标签支持
嵌入图片的object、img、input[type=image]、script、link
和css
规则中的background
和backgroundImage
属性
九、IE678的polyfill
方案——MHTML
MHTML(MIME HTML,Multipurpose Internet Mail Extensions HyperText Markup Language)
,就是将Data URI
以附件的形式附加到页面页面上,具体示例如下:
/** FilePath: http://example.com/test.css */
/*!@ignore
Content-Type: multipart/related; boundary="_ANY_SEPARATOR"
--_ANY_SEPARATOR
Content-Location:myidBackground
Content-Transfer-Encoding:base64
iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==
--_ANY_SEPARATOR--
*/
.myid {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==");
*background-image: url(mhtml:http://example.com/test.css!myidBackground);
}
上面注释的部分就是定义一个名为myidBackground
的Base64
编码图片,然后在class
为myid
的css
中使用。
注意:
1、boundary
字段值可自定义;
2、附件的末行必须为boundary
字段值;
3、附件内容不能被压缩工具擦写掉;
4、由于高版本的IE在使用IE8兼容模式时能认识*这个css hack
,但却不支持mhtml
,所以会导致背景图片失效。应该采用IE的条件注释更为稳妥。
十、安全问题
当在IE6/7的HTTPS
页面中使用Data URI
时会提醒
MS 的解释是:
您正在查看的网站是个安全网站。它使用了 SSL (安全套接字层)或 PCT(保密通讯技术)这样的安全协议来确保您所收发信息的安全性。
当站点使用安全协议时,您提供的信息例如姓名或信用卡号码等都经过加密,其他人无法读取。然而,这个网页同时包含未使用该安全协议的项目。
也就是说问题在scheme
字段上,由于全站都采用https
的scheme
,而data scheme
则被视为不安全的协议了。
十一、应用
- 绕过浏览器过滤
// 绕过浏览器过滤
http://example.com/text.php?t="><script src="data:text/html,<script>alert("Xss")</script><
- 批量请求图片
$.get('http://imgs.foo.com', {ids:[1,2,3,4,5,6,7]}, function(data){
var imgs = []
data.each(function(i, dataUri){
imgs.push($(['<img src="https://img-blog.csdnimg.cn/2022010612382527833.jpeg', dataUri, '"/>'].join('')))
})
$(body).append(imgs)
})
十二、完全理解Base64
编码
Base64字符集: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
字节与字符映射关系(十进制):从0
开始到63
原理:
对以某编码方式编码后的字节数组为对象,以3个字节为一组,按顺序排列24bit数据,然后以6bit一组分成4组;再在每组的最高位补2个0凑足一个字节。这时一组就有4个字节了。若字节数组不是3的倍数,那么最后一组就填充1到2个0字节。
然后按Base64编码方式(就是映射关系)对字节数组进行解码,就会得到平时看到的Base64编码文本。对于字节数组不是3的倍数,最后一组填充1到2个0字节的情况,填补的0字节对应的是=(等号)。
示例:
①. 对AB进行ASCII编码:得到A(65)B(66)
②. 转成二进制形式:得到A(01000001)B(01000010)
③. 以3个字节为一组,非3的倍数补0字节:010000010100001000000000
④. 以6bit为一组后高位补两个0:(00 010000)(00 010100)(00 001000)(00 000000)
⑤. 转为十进制:(16)(20)(8)(0)
⑥. 根据映射关系解码:QUI=