将Base64转为File类型文件

20,305 阅读4分钟

前言

base64是一种用64个可打印字符编码任意二进制的方法。所谓Base64,就是说选出64个字符----小写字母a-z、大写字母A-Z、数字0-9、符号"+"、"/"(再加上作为垫字的"=",实际上是65个字符)----作为一个基本字符集。然后,其他所有符号都转换成这个字符集中的字符。 具体来说,转换方式可以分为四步。

  • 第一步,将每三个字节作为一组,一共是24个二进制位。
  • 第二步,将这24个二进制位分为四组,每个组有6个二进制位。
  • 第三步,在每组前面加两个00,扩展成32个二进制位,即四个字节。
  • 根据上表,得到扩展后的每个字节的对应符号,这就是Base64的编码值。

因为,Base64将三个字节转化成四个字节,因此Base64编码后的文本,会比原文本大出三分之一左右

Base64的码表只有64个字符, 如果要表达64个字符的话,使用6bit即可完全表示(2的6次方为64)。因为Base64的编码只有6bit即可表示,而正常的字符是使用8bit表示, 8和6的最小公倍数是24,所以4个Base64字符可以表示3个标准的ascll字符。

将字符串转换为base64时,会先把字符串转换为对应的ascll码,然后从左往右6位截取(6位对应一位base64码),若最后不足6位(一个base64码)补0,不足3个字符串则补=(解码时会自动去掉)

由于=字符也可能出现在Base64编码中,但=用在URLCookie里面会造成歧义,所以,很多Base64编码后会把=去掉。因为base64码的位数永远是4的倍数,所以解码时若位数不足可以在末尾补=

将图片转换成Base64的编码方式是因为可以将图片直接嵌入到网页中,而不是从外部载入,这样就减少了HTTP请求。

例子:汉字"严"如何转化成Base64编码

这里需要注意,汉字本身可以有多种编码,比如gb2312utf-8gbk等等,每一种编码的Base64对应值都不一样。下面的例子以utf-8为例。

首先,"严"的utf-8编码为E4B8A5,写成二进制就是三字节的"11100100 10111000 10100101"。将这个24位的二进制字符串,转换成四组一共32位的二进制值"00111001 00001011 00100010 00100101",相应的十进制数为57、11、34、37,它们对应的Base64值就为5、L、i、l

所以,汉字"严"(utf-8编码)的Base64值就是5Lil

原生atobbtoa方法

IE10+浏览器开始,所有浏览器就原生提供了Base64编码解码方法,不仅可以用于浏览器环境,Service Worker环境也可以使用。

方法名就是atobbtoa,具体语法如下:

1. Base64解码

// 浏览器中
var decodedData = window.atob(encodedData);
// 浏览器或js Worker线程中
var decodedData = self.atob(encodedData);

例如

window.atob('RnJ1aXQgQnJv');
// 返回:'Fruit Bro

atob这个方法名称乍一看,很奇怪,不知道这个单词什么意思。我们可以理解为 A to B,也就是从AB。也就是把base64转为字符串。

2. Base64编码

// 浏览器中
var encodedData = window.btoa(stringToEncode);
// 浏览器或js Worker线程中
var encodedData = self.btoa(stringToEncode);

3. 任意文件Base64编码

借助FileReader对象和readAsDataURL方法,我们可以把任意文件转为Base64 Data-URI。假设我们的文件对象是file,则转换的JavaScript代码如下:

var reader = new FileReader();
reader.onload = function(e) {
  // e.target.result就是该文件的完整Base64 Data-URI
};
reader.readAsDataURL(file);

Uint8Array

developer.mozilla.org/zh-CN/docs/…

Uint8Array 数组类型表示一个8位无符号整型数组,创建时内容被初始化为0。创建完后,可以以对象的方式或使用数组下标索引的方式引用数组中的元素。

new Uint8Array(); // ES2017 最新语法
new Uint8Array(length); // 创建初始化为0的,包含length个元素的无符号整型数组
new Uint8Array(typedArray);
new Uint8Array(object);
new Uint8Array(buffer [, byteOffset [, length]]);

charCodeAt

developer.mozilla.org/zh-CN/docs/…

charCodeAt() 方法返回 0 到 65535 之间的整数,表示给定索引处的 UTF-16 代码单元。

UTF-16编码单元匹配能用一个UTF-16编码单元表示的 Unicode 码点。如果 Unicode 码点不能用一个 UTF-16 编码单元表示(因为它的值大于0xFFFF),则所返回的编码单元会是这个码点代理对的第一个编码单元) 。如果你想要整个码点的值,使用 codePointAt()

最终实现

function dataURLtoFile(dataurl: string, filename: string) {
  // 获取到base64编码
  const arr = dataurl.split(',')
  // 将base64编码转为字符串
  const bstr = window.atob(arr[1])
  let n = bstr.length
  const u8arr = new Uint8Array(n) // 创建初始化为0的,包含length个元素的无符号整型数组
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n)
  }
  return new File([u8arr], filename, {
    type: 'image/jpeg',
  })
}
const photoName = `${new Date().getTime()}`
const picContent = dataURLtoFile(
  'data:image/gif;base64,' + pic,
  `${photoName}.jpg`
)

参考文档:

stackoverflow.com/questions/3…

stackoverflow.com/questions/1…

github.com/akira-cn/FE…

www.zhangxinxu.com/wordpress/2…

www.ruanyifeng.com/blog/2008/0…