- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
在我们进行前端开发时,针对项目优化,常会提到一条:针对较小图片,合理使用Base64字符串替换内嵌,可以减少页面http请求。
并且还会特别强调下,必须是小图片,大小不要超过多少KB,等等。
那么,Base64又到底是什么呢?
下面的这段字符串,应该是大家都很常见的。通过这种固定的格式,来表示一张图片,并被浏览器识别,可以完整的展示出图片:
data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0c......
这里展示的是一个svg格式的图片,当然我们还可以加载任何浏览器支持的格式的图片。
这段字符串就是基于Base64编码得来的,其中base64,
后面那一长串的字符串,就是Base64编码字符串。
互联网发展早起,电子邮件是最有效的应用。
而电子邮件的SMTP传输协议在早期,只能用于传送7位的ASCII码,而ASCII码就是基于英语设计的,对于非英语国家的文字等资源就无法发送。
为了解决这个问题,后来有了通用互联网邮件扩充MIME,增加了邮件的主体结构,定义了非ASCII码的编码传输规则,这就是Base64。
关于字符编码的知识,请查看前端开发中需要搞懂的字符编码知识
Base64是基于64个可打印字符来表示二进制数据的编解码方式。
正因为可编解码,所以它主要的作用不在于安全性,而在于让内容能在各个网关间无错的传输。
这64个可打印字符包括大写字母A-Z
、小写字母a-z
、数字0-9
共62个字符,再加上另外2个 +
和 /
。
Base64是一种索引编码,每个字符都对应一个索引,具体的关系图,如下:
这也是名称中64的由来。
由于64等于2的6次方,所以一个Base64字符实际上代表着6个二进制位(bit)。
然而,二进制数据1个字节(byte)对应的是8比特(bit),因此,3字节(3 x 8 = 24比特)的字符串/二进制数据正好可以转换成4个Base64字符(4 x 6 = 24比特)。
为什么是3个字节一组呢? 因为6和8的最小公倍数是24,24比特正好是3个字节。
具体的编码方式:
上图中的字符串 'you'
,经过转换后,得到的编码为: 'eW91'
。
我们可以看到,当3个字符进行Base64转换编码后,最后变成了4个字符。因为每个6比特位,都补了2个0,变成8比特位,对应1字节。
这里正好多了三分之一,所以正常情况下,Base64编码的数据体积通常比原数据的体积大三分之一。
这也是为什么我们在前面讲使用Base64编码优化图片时,需要强调是小图标,如果图片都使用该方式,则静态文件会增大很多,并不合适。
3个英文字符,正好能转成4个Base64字符。那如果字符长度不是3的倍数,那应该使用什么样的规则呢?
其实也简单,我们在实际使用Base编码时,常会发现有第65个字符的存在,那就是 '='
符号,这个等于号就是针对这种特殊情况的一种处理方式。
对于不足3个字节的地方,实际都会在后面补0,直到有24个二进制位为止。
但要注意的是,在计算字节数时,会直接使用总长度除以3,如果余数为1则会直接在最后补一个=
,如果余数为2则补两个=
。
因此,转码后的字符串需要补的后缀等号,要么是1个,要么是2个,具体的可以见下图:
图中第二个,使用的是单独的字符 'd'
,是为了区分索引字符表里的索引0,这个时候,得到编码中,会存在一个索引0对应的A字符,而'='
是直接补上2个。
由于 Base64
仅可对 ASCII
字符进行编码,如果是中文字符等非ASCII码,就需要先将中文字符转换为ASCII字符后,再进行编码才行。
JavaScript提供了两个原生方法,用来处理Base64编码:btoa()
和 atob()
。
btoa()
: 将字符串或二进制值转换成Base64编码字符串。atob()
: 对base64 编码的字符串进行解码。btoa('you') // 'eW91'
atob('eW91') // 'you'
btoa('中') // Uncaught DOMException: The string to be encoded contains characters outside of the Latin1 range.
atob('y') // Uncaught DOMException: The string to be decoded is not correctly encoded.
由于btoa、atob 仅支持对ASCII字符编码,也就是单字节字符,而我们平时的中文都是 2-4 字节的字符。
因此,可以先将中文字符转为 utf-8
的编码,将utf-8编码当做字符,这样就可以对多个单字节字符进行编码。
对于中文可以使用这两个方法: encodeURIComponent()
和 decodeURIComponent()
。
如下,编解码中文的方式:
window.btoa(encodeURIComponent('中国'))
// 'JUU0JUI4JUFEJUU1JTlCJUJE'
decodeURIComponent(window.atob('JUU0JUI4JUFEJUU1JTlCJUJE'))
// '中国'
接下来,我们了解下前端开发中常见的对Base64编码的一些使用场景。
Base64在前端方面的应用,多数都是针对图片的处理,一般都是基于DataURL的方式来使用。
Data URL 由 data:前缀
、MIME类型(表明数据类型)
、base64标志位
(如果是文本,则可选)以及 数据本身
四部分组成。
具体的格式:data:[<mime type>][;base64],<data>
。
这里的第四部分 <data>
数据本身,就是一个Base64字符串。
即开篇说的针对图片优化,使用Base64能减少请求数的,可以在img标签下,或者css中:
<img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0c......Ii8+PC9nPjwvc3ZnPg==">
.icon {
background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0c......Ii8+PC9nPjwvc3ZnPg==);
}
当我们使用vue或react框架时,也可以通过url-loader来配置,图标转Base64的大小:
.loader('url-loader')
.tap(options => {
options.limit = 10240 // 10kb
return options
})
Web环境下,有提供 FileReader
的API,用来读取文件的数据,可以通过它的 readAsDataURL()
方法,将文件数据读取为Base64编码的字符串数据:
let reader = new FileReader()
reader.onload = () => {
let base64Img = reader.result
};
reader.readAsDataURL(file)
该方法常用在图片上传中。
Canvas本质上是一个位图图像,它有提供 toDataURL()
方法,将画布导出生成为一张图片,该图片将以Base64编码的格式进行保存。
const dataUrl = canvasEl.toDataURL()
// data:image/png;base64,PHN2ZyB4bWxucz0iaHR0c......
处理图片展示外,还会在特殊数据传输、简单编码和加密、代码混淆、部分证书中,见到Base64编码字符串。
最后再来总结一下Base64的特点:
如果我不定义自己的构造函数,Base *b = new Base; 与 Base *b = new Base(); 之间有什么区别吗? 最佳答案 初始化是标准中要遵循的一种 PITA...然而,这两个
是否有现成的函数可以在 C# 中进行基本转换?我希望将以 26 为基数和以 27 为基数的数字转换为以 10 为基数。我可以在纸上完成,但我不是一个非常有经验的程序员,如果可能的话,我宁愿不要从头开始
JNA 中'base'是什么意思 Pointer.getPointerArray(long base) Pointer.getStringArray(long base) ? JNA Document
我正在做一个将数字从 10 进制转换为 2 进制的基本程序。我得到了这段代码: #include #include #include #include using namespace std;
“假设以下代码: public class MultiplasHerancas { static GrandFather grandFather = new GrandFather();
当我分析算法的时候,我突然问自己这个问题,如果我们有三元计算机时间复杂度会更便宜吗?还是有任何基础可以让我们构建计算机,这样时间复杂度分析就无关紧要了?我在互联网上找不到太多,但是基于三元的计算机在给
一个简化的场景。三个类,GrandParent,Parent 和 Child。我想要做的是利用 GrandParent 和 Parent 构造函数来初始化一个 Child 实例。 class Gran
我编写了一个简单的函数来将基数为 10 的数字转换为二进制数。我编写的函数是我使用我所知道的简单工具的最佳尝试。我已经在这个网站上查找了如何执行此操作的其他方法,但我还不太了解它。我确定我编写的函数非
我尝试了以下代码将数字从 base-10 转换为另一个 base。如果目标基地中没有零(0),它就会工作。检查 79 和 3 并正确打印正确的 2221。现在尝试数字 19 和 3,结果将是 21 而
这个问题在这里已经有了答案: Is Big O(logn) log base e? (7 个答案) 关闭 8 年前。 Intro 练习 4.4.6 的大多数解决方案。算法第三版说,n*log3(n)
如何判断基类(B)的指针是否(多态)重写了基类的某个虚函数? class B{ public: int aField=0; virtual void f(){}; }; class C
我测试了这样的代码: class A { public A() { } public virtual void Test () { Console.WriteL
两者都采用相同的概念:定义一些行和列并将内容添加到特定位置。但是 Grid 是最常见的 WPF 布局容器,而 html 中基于表格的布局是 very controversial .那么,为什么 WPF
我试图在 JS 中“获得”继承。我刚刚发现了一种基本上可以将所有属性从一个对象复制到另一个对象的简洁方法: function Person(name){ this.name="Mr or Miss
class A { public override int GetHashCode() { return 1; } } class B : A { pu
我有一个 Base32 信息哈希。例如IXE2K3JMCPUZWTW3YQZZOIB5XD6KZIEQ ,我需要将其转换为base16。 我怎样才能用 PHP 做到这一点? 我的代码如下所示: $ha
我已经使用其实验界面对 Google Analytics 进行了一些实验,一切似乎都运行良好,但我无法找到 Google Analytics 属性如何达到变体目标的答案,即归因 session - 基
if (state is NoteInitial || state is NewNote) return ListView.builder(
MSVC、Clang 和 GCC 不同意此代码: struct Base { int x; }; struct Der1 : public Base {}; struct Der2 : public
我已经尝试构建一个 Base 10 到 Base 2 转换器... var baseTen = window.prompt("Put a number from Base 10 to conver
我是一名优秀的程序员,十分优秀!