最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 前端中的二进制流

    正文概述 掘金(清风晴雨)   2021-03-30   578

    说说二进制流

    我们通常将图片/音频等文件用二进制流来表示.

    计算机的存储在物理上是都二进制的,所以文本文件与二进制文件的区别并不是物理上的,而是逻辑上的。

    可能有人要问了,为什么要用二进制来表示图片呢?我们来看看二进制的优势:

    1. 二进制文件比较节约空间,在储存字符型数据时与普通存储并没有差别。但是在储存数字,特别是实型数字时,二进制更节省空间;
    2. 内存中参加计算的数据都是用二进制无格式储存起来的,因此,使用二进制储存到文件就更快捷。如果储存为文本文件,则需要一个转换的过程。在数据量很大的时候,两者就会有明显的速度差别了;
    3. 涉及到一些比较精确的数据,使用二进制储存不会造成有效位的丢失;

    看到这里,相信大家已经对二进制流有了一定的认识。既然提到了存储,补充说明一下图片在后端存储方式:

    • 其一:可以将图片以独立文件的形式存储在服务器的指定文件夹中,再将路径存入数据库字段中;
    • 其二:将图片转换成二进制流,直接存储到数据库的 Image 类型字段中;

    base64 编码

    我们有时候会在网页中看到这样的代码:

    data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAb4AAABYCAYAAACK55LCAAAgAElEQVR4Xuy9aXNc2ZEleN4e+wIgsO8ACXAnk8mUUqoqldQ9Yz3W/an/wsxvnA/TM2NWizKVmczkTgIg9h0IxL6+fez4DbCo7JQqpZLZfGBARjEJBOK95/de9+PHj3toO//7f40x/BpaYGiBoQWGFhha4BOxgDYMfJ/ISg8fc2iBoQWGFhhaQCwwDHzDjTC0wNACQwsMLfBJWWAY
    ...
    

    这就是 base64 格式的数据,代表着一张图片的数据,专业术语叫:Data URI scheme

    Base64是一种基于64个可打印字符来表示二进制数据的表示方法。

    表示方式:

    由于前端中的二进制流,所以每6个 二进制位(bit) 为一个单元,对应某个可打印字符。3个 字节(byte)有24个bit,对应于4个Base64单元,即3个字节可由4个可打印字符来表示。

    编码字符的情况下,由于 Base64 仅可对 ASCII 字符进行编码,也就是单字节字符(可以理解为英文和数字,还有一些简单符号)。若编码中文字符串,则需先转换为 ASCII 字符。

    前端中的二进制流

    在Base64中的可打印字符包括 字母A-Za-z、数字0-9,这样共有62个字符,此外两个可打印符号在不同的系统中而不同。

    转换方式:

    • 编码

      const base64 = window.btoa('abc'); // YWJj
      
    • 解码

      const initString = window.atob(base64); // abc
      

    但是 btoa、atob 仅支持对 ASCII 字符编码,也就是单字节字符,而我们平时的中文都是 3-4 字节的字符。

    因此可以先将中文字符转为 utf-8 的编码,将 utf-8 的编码当做字符,这样就可以对多个单字节字符进行编码。往下看。

    encodeURIComponent & decodeURIComponent

    chrome 上输入网址 http://zh.wikipedia.org/wiki/春节,然后复制下来,你会发现变成了 https://zh.wikipedia.org/wiki/%E6%98%A5%E8%8A%82

    我们知道,"春"和"节"的 utf-8 编码分别是"E6 98 A5"和"E8 8A 82",因此,"%E6%98%A5%E8%8A%82"就是按照顺序,在每个字节前加上 % 而得到的。

    这也就是 encodeURL 的功能:将非 ACSII 码的字符进行 utf-8 编码。 encodeURLComponent 与它不同的地方在于,对整个 url 进行编码(http%3A%2F%2Fzh.wikipedia.org%2Fwiki%2F%E6%98%A5%E8%8A%82)。

    所以接上文,我们可以对 url 进行编码后,将这个 utf-8 编码作为 ACSII 字符,进行 base64。

    比如我们在转换中文的时候:

    • 编码(多字节)

      const base64 = window.btoa(encodeURIComponent('春节')); // JUU2JTk4JUE1JUU4JThBJTgy
      
    • 解码(多字节)

      const initString = decodeURIComponent(window.atob(base64)); // 春节
      

    Blob 对象(二进制容器)

    Blob (binary large object),二进制大对象,是一个可以存储二进制文件的容器

    构建方式:

    const blob = new Blob(['something...'])
    

    这是我转换的一个 Blob 对象:

    Blob {
      name: "图片示例:jartto.png",
      preview: "blob:file:///f3823a2a-2908-44cb-81e2-c19d98abc5d1",
      size: 47396,
      type: "image/png",
    }
    

    请注意,这里的 preview 属性是可以拿出来做图片预览的。

    base64 与 Blob 互转

    在日常开发中, 最常见的便是将blobbase64之间相互转换.

    // blob to base64
    function blobTobase64(blob) {
        const fileReader = new FileReader()
    	let base64 = ''
    	fileReader.onload = () => {
      		base64 = fileReader.result // 读取base64
    	}
    	fileReader.readAsDataURL(blob) // 读取blob
    }
    // base64 to blob
    function dataURItoBlob(dataURI) {
      var mimeString = dataURI
        .split(',')[0]
        .split(':')[1]
        .split(';')[0] // mime类型
      var byteString = atob(dataURI.split(',')[1]) //base64 解码
      var arrayBuffer = new ArrayBuffer(byteString.length) //创建ArrayBuffer
      var intArray = new Uint8Array(arrayBuffer) //创建视图
      for (var i = 0; i < byteString.length; i++) {
        intArray[i] = byteString.charCodeAt(i)
      }
      return new Blob([intArray], { type: mimeString }) // 转成 blob
    }
    

    URL 转 base64

    好了,更有意思的事情来了,我提高了要求,只给你一个图片的 URL(本地或者在线地址),是否能将图片编码成流?答案是肯定的,这里我们得请出 canvas 了:

    思路很简单,根据图片对象的数据去画 canvas,再利用它来等价表示图片流。

    getDataUri(url) {
      return new Promise((resolve, reject) => {
        /* eslint-disable */
        let image = new Image();
        image.onload = function() {
          let canvas = document.createElement('canvas');
          canvas.width = this.naturalWidth;
          canvas.height = this.naturalHeight;
          canvas.getContext('2d').drawImage(this, 0, 0);
          // Data URI
          resolve(canvas.toDataURL('image/png'));
        };
        image.src = url;
        // console.log(image.src);
        image.onerror = () => {
          reject(new Error('图片流异常'));
        };
      });
    }
    

    你可以像这样调用:

    let dataUri = await this.getDataUri(`image/test/jartto.png`);
    

    FileReader 是什么?

    从上节了解到Blob对象只是二进制数据的容器,本身并不能操作二进制,故本节将对其操作对象FileReader进行介绍。

    读取的 File 对象可以是什么呢?

    • 来自用户在一个 <input> 元素上选择文件后返回的 FileList 对象

    • 来自拖放操作生成的 DataTransfer 对象

    • 来自在一个 HTMLCanvasElement 上执行 mozGetAsFile() 方法后返回结果

    • ... ...

    官话说了一大堆,其实你只需要知道「它是用来读取文件的」就够了。

    参数说明如下:

    • FileReader.error(只读):一个 DOMException,表示在读取文件时发生的错误 ;
    • FileReader.readyState(只读):表示 FileReader 状态的数字。取值如下:
      • EMPTY 0 还没有加载任何数据.
      • LOADING 1 数据正在被加载.
      • DONE 2 已完成全部的读取请求.
    • FileReader.result(只读):文件的内容。该属性仅在读取操作完成后才有效,数据的格式取决于使用哪个方法来启动读取操作。

    还有几个事件处理函数:

    • FileReader.onabort:处理 abort 事件。该事件在读取操作被中断时触发。
    • FileReader.onerror:处理 error 事件。该事件在读取操作发生错误时触发。
    • FileReader.onload:处理 load 事件。该事件在读取操作完成时触发。
    • FileReader.onloadstart:处理 loadstart 事件。该事件在读取操作开始时触发。
    • FileReader.onloadend:处理 loadend 事件。该事件在读取操作结束时(要么成功,要么失败)触发。
    • FileReader.onprogress:处理 progress 事件。该事件在读取 Blob 时触发。

    可以用的方法如下:

    • FileReader.abort():中止读取操作。在返回时,readyState 属性为 DONE。
    • FileReader.readAsArrayBuffer():开始读取指定的 Blob 中的内容。一旦完成,result 属性将包含一个ArrayBuffer 来表示文件数据;
    • FileReader.readAsBinaryString()(即将弃用):开始读取指定的 Blob 中的内容。一旦完成,result 属性中将包含所读取文件的原始二进制数据。
    • FileReader.readAsDataURL():开始读取指定的 Blob 中的内容。一旦完成,result 属性中将包含一个 data: URL 格式的字符串以表示所读取文件的内容。(base64编码)
    • FileReader.readAsText():开始读取指定的 Blob 中的内容。一旦完成,result 属性中将包含一个字符串以表示所读取的文件内容。

    上面的文档描述的很清楚了,但是估计你也没逐条细看,没关系,我们还有示例?

    具体的应用场景可能如下:

    const blob = new Blob(['你']);
    
    const reader = new FileReader();
    
    reader.readAsArrayBuffer(blob);
    
    reader.onload = (e) => {
      // ArrayBuffer(3) {}
      let result = e.target.result;
    
      // 这里使用Unit8解读ArrayBuffer
      const v1 = new Uint8Array(result);
    
      // "你" 这个字符默认转为utf-8编码,占据三个字节;这里为10进制展示,只是为了直观
      console.log(v1) // Unit8Array(3) [228, 189, 160]
    }
    ---------------------------------------------------------------
    // 同样我们可以将二进制解读出文本(假设我们拿到了 v1 Unit8二进制数组对象)
    const blob = new Blob([v1]);
    
    let reader = new FileReader();
    
    reader.readAsText(blob);
    
    reader.onload = (e) => {
      console.log(e.target.result) // 你
    }
    
    

    用法很简单,你唯一需要注意的是一定要在 onload 之后再做操作!

    常见问题与补充说明

    1. Failed to execute 'readAsDataURL' on 'FileReader': parameter 1 is not of type 'Blob'.

      出现如上问题,请检查你的 Blob 对象格式是否正确,是否缺少必要项。

    2. 很多第三方应用都需要去除 base64 头信息:

      base64.replace(/^data:image\/(jpeg|png|gif);base64,/,'')
      
    3. FileReader 读取文件后,记得放在 onload 函数内,切记。

    4. 在使用 canvas.getContext('2d').drawImage(this, 0, 0); 过程中,一定要注意这里的 this 指向,否则可能发生异常。

    5. base64Blob,请记得:

      Object.assign(blob,{
        // jartto: 这里一定要处理一下
        preview: URL.createObjectURL(blob),
        name: `图片示例:${index}.png`
      });
      

    参考

    1. MDN FileReader
    2. ASCII,Unicode,UTF-8和Base64
    3. Convert Image to Data URI
    4. html base64 img 图片显示
    5. 解决 Javascript 中 atob 方法解码中文字符乱码问题

    起源地下载网 » 前端中的二进制流

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元