base64的作用与原理,实战

1,137 阅读6分钟

base64的作用

通俗的讲:就是把一张图片或文件,变成一串字符串,这样就可以直接把这个字符串保存到数据库中

这种编码通常用于在网络中传输二进制数据,如图片或文件,

优点

  1. 因为它可以避免数据在不同系统间传输时被误解或破坏;
  2. 同时无需借用一个空间进行存储,然后再通过地址获取到这个图片或者文件资源。

缺点

通过base64转化后,体积会大了 1/3 。

虽然Base64编码可以在一定程度上隐藏原始数据,但它并不提供任何加密功能。如果需要保护数据内容的安全,还需采用其他的加密技术。因为它的编码与解码过程是确定且公开的

使用场景

  1. 常用于在HTTP协议中传输二进制数据
  2. 如音乐文件里面保存一张封面图片,就是通过base64来进行保存

base64转换原理

将二进制数据转化为由64个字符(ASCII码)组成的可打印字符串的方法。

  1. 源数据是以二进制的数据形式输入进来
  2. 每3个字节(即24位)被分为一组,每组被映射到一个4个字符的可打印字符集中。
  • 分组: 首先,将数据按每三个字节分为一组,每组24比特。如果数据长度不是3的倍数,通过添加填充字节(通常用'='表示)来确保每组都是三个字节。
  • 转换为比特组: 每24比特按每6比特一组分成四组,每组映射到Base64索引表中的一个字符。
  • 补位: 由于每组原本只有6比特,为符合字节尺寸(8比特),在每组前面补充两个0比特,从而形成标准的单字节格式。
  • 索引查找: 根据上述得到的每组6比特的值,查找Base64索引表,获得对应的字符。这些字符连在一起就形成了Base64编码后的字符串。
  • 处理非整组数据: 如果数据字节数不是3的倍数,转换过程中最后一组可能不满6比特,此时按照标准做法在末尾添加'='字符进行填充,以保证编码后的长度是4的倍数
const Base64 = {
  keyStr: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
  encode(input) {
    let output = '';
    let chr1, chr2, chr3, enc1, enc2, enc3, enc4;
    let i = 0;
    input = Base64.utf8Encode(input);
    while (i < input.length) {
      chr1 = input.charCodeAt(i++);
      chr2 = input.charCodeAt(i++);
      chr3 = input.charCodeAt(i++);
      // 只取第一个的前6位
      enc1 = chr1 >> 2;
      // 取第一个的后两位放到第三第四位上 + 第二个字节的前四位
      // chr1 & 3 也就是相当于 char1 && 11(二进制)相当于提取char1的最后两位的比特值。
      // (char1 & 3) << 4 也就是相当于 把(char & 3)的值向左移动4位 相当于把高位舍弃,把低位移动到高位,
      // char2 >> 4 相当于把 char2 的二进制向右移动4位,也就是把低位舍弃,把高位移动到低位
      // ((chr1 & 3) << 4) | (chr2 >> 4) 相当于把两个得出来的值,进行按位或运算,将他们的比特制按位组合起来
      enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
      // 取第二个的后4位 + 第三个的前两位
      enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
      // 取第三个的后六位
      enc4 = chr3 & 63;
      if (isNaN(chr2)) {
        enc3 = enc4 = 64;
      } else if (isNaN(chr3)) {
        enc4 = 64;
      }
      output = output +
        Base64.keyStr.charAt(enc1) + Base64.keyStr.charAt(enc2) +
        Base64.keyStr.charAt(enc3) + Base64.keyStr.charAt(enc4);
    }
    return output;
  },
}
// 编码
window.atob()
// 解码
window.btoa()

对base64图片进行压缩

压缩:减少体积

  1. 有损地减少体积
  2. 无损地减少体积

1. 有损地减少体积

方向: 减少图片的尺寸、减少图片的质量(像素的数量(分辨率)、RGB 颜色空间转换(亮度(Y)和色度(Cb和Cr)分量)...等)如:JPEG 、WebP的压缩算法

实现方式:

  1. 通过Image对象,把base64的图片转换回一张图片资源,
  2. 然后通过设置重新设置图片的尺寸大小(宽度,高度)来减少图片的体积(最好就是我们在实际应用中需要渲染的图片的宽高(如果是可以确定的情况下))
  3. 在通过canvas绘制图片,再把图片转换成base64,
    1. 这里canvas.toDataURL(base64图片的格式, 压缩比); 是提供了一个参数可以调节压缩比,来对图片的质量进行压缩
  1. 最终实现base64的压缩

经过对同一张图片的测试:嘚出最终用canvas转换的base64图片是WebP格式的,体积是最小的,且肉眼无法看出区别。

/**
 * 
 * @description 压缩base64图片,使用Image对象转换base64,再对图片的宽高进行压缩,再用canvas转回去base64
 * @param base64Image base64图片
 * @param maxWidth 图片的最大宽度
 * @param maxHeight 图片的最大高度
 * @param quality 图片的压缩比(0 - 1)
 * @returns Promise 返回经过压缩后的base64
 */
function compressBase64Image(base64Image:string, maxWidth:number, maxHeight:number, quality:number) {
    return new Promise((resolve, reject) => {
      const img = new Image();
      img.src = base64Image;
      img.onload = function() {
        let width = img.width;
        let height = img.height;
        // 如果图片尺寸小于最大尺寸,则直接返回原始 base64 图片
        if (width <= maxWidth && height <= maxHeight) {
          resolve(base64Image);
        }
        // 计算压缩后的宽度和高度
        if (width > height) {
          if (width > maxWidth) {
            height *= maxWidth / width;
            width = maxWidth;
          }
        } else {
          if (height > maxHeight) {
            width *= maxHeight / height;
            height = maxHeight;
          }
        }
        // 创建一个 Canvas 元素
        const canvas = document.createElement("canvas");
        canvas.width = width;
        canvas.height = height;

        // 在 Canvas 上绘制图片
        const ctx = canvas.getContext("2d");
        ctx?.drawImage(img, 0, 0, width, height);
        // 将 Canvas 转换为压缩后的 base64 图片
        const compressedBase64Image = canvas.toDataURL("image/jpeg", quality);
  
        resolve(compressedBase64Image);
      };
      img.onerror = function() {
        reject(new Error("Failed to load image"));
      };
    });
  }

2. 无损地减少体积

方向:减少图片渲染的位数,同时通过改变其他像素的值来替代了减少的位数的功能。PNG的压缩算法

计算base64图片的实际大小size

根据base64的编码原理就可推出

function getBase64ImageSize(base64:any) {
	if (base64) {
    // 裁剪出真实的base64字符串,“,” 前的是base64的头部, “=” 是当图片原数据不是3个字节为一组就会用“=”当作一个字节来补全
		base64 = base64.split(",")[1].split("=")[0];
		const strLength = base64.length;
    // ASCII字符串,每个字符是用一个字节代表的。多少个字符 = 多少个字节,一个字节 = 8个bit
    // strLength / 8 * 2 可以理解为:每8个字符就会有两个字符是多加进去的
    // 也可以理解为:strLength * 1 * 8 / 8 * 2 / 8 ;一共有strLength * 1个字节,也就是有 strLength * 1 * 8个bit,每8个bit就会有两个是多出来的,也就是strLength * 1 * 8 / 8 个字节出来。
    // 实际字节数 = 总字节 - 多出来的字节
		const fileLength = strLength - (strLength / 8) * 2;
        // 向下取整
		return Math.floor(fileLength);
	} else {
		return 0;
	}
}