前端二进制-实战

Posted lin-fighting

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端二进制-实战相关的知识,希望对你有一定的参考价值。

二进制对象

ArrayBuffer
TypedArray
DataView
Blob
Object Url

ArrayBuffer

  • ArrayBuffer对象用来表示通用的,固定长度的原始二进制缓冲区。
  • 他是一个字节数组,通常在其它语言中称为byte array
  • 不能直接操作ArrayBuffer的内容,而是要通过类型数组对象(TypedArray)或者DataView对象来操作。他们会将缓区中的数据表示为特定的格式,并通过这些格式来读取内容。
const buffer = new ArrayBuffer(8) // 8个字节,64位
console.log(buffer.byteLength);不能直接操做

操作ArrayBuffer的对象

TypedArray, DataView

TypedArray

  • TypedArray对象描述了一个底层的二进制缓存区的一个类数组视图
  • 它本身无法被实例化,甚至无法访问,可以把他理解为接口,他有很多的实现。
  • Int8Array 8位二进制有符号整数 -128 - 127 每单位1个bit
  • Unit7Array 8位无符号整数 0-255 每单位1个bit
  • Int16Array 16位二进制有符号整数 -32768 - 32767 每单位2个bit
  • Unit16Array 16位无符号整数 0 - 65535 每单位2个bit
const buffer = new ArrayBuffer(8)
const int8Array = new Int8Array(buffer)
const int16Array = new Int16Array(buffer)
console.log(int8Array .buffer);
console.log(int8Array.length);
console.log(int16Array.length);
// 
8 8 4
因为Int16Array每个单位是两个字节,所以八个字节就有四个单位,所以长度是4 

ArrayBuffer无法直接操作,可以通过Int8Array等类来操作。

DataView(任意读取多种数值类型)

  • DataView视图是一个可以从二进制ArrayBuffer对象中读写多种数值类型的底层接口
  • setInt8()从DataView起始位置以byte为计数的指定偏移量(byteOffset)处存储了一共8-bit数
  • getInt8()从DataView起始位置以byte为计数的指定偏移量(byteOffset)处获取了一共8-bit数
const buffer = new ArrayBuffer(2) //设置一个2字节的二进制对象
const dataView = new DataView(buffer)
console.log(dataView.buffer)
dataView.setInt8(0, 2) //从0开始,设置了一个值为2,结果应该是
dataView.setInt8(1,3) //从1个字节开始,设置了3
console.log(dataView.getInt8(0)); //2
console.log(dataView.getInt8(1));  // 3
console.log(dataView.getInt16(0)); // 515
//因为
2是 00000010
3是 00000011
结合起来就是 00000010  00000011 这样一个二进制数就是515

三者关系

DataView => dataView.buffer => ArrayBuffer
ArrayBuffer => new DataView(buffer) => DataView

TypedArray => new TypedArray(buffer) =>ArrayBuffer
ArryaBuffer => typedArray.buffer => TypedArray

Blob

  • Blob对象表示一个不可变的,原始数据类型的类文件对象,Blob表示的不一定是js原生格式的数据,
  • Fibe接口基于Blob,继承了Blob的功能并将其扩展使其支持用
const debug = name: '123'
let str = JSON.stringify(debug)
const blob = new Blob([str],type: 'application/json')
console.log(blob);

// 

size: 14
type: "application/json"


// 转换ArrayBuffer
const blob = new Blob([buffer],type: 'application/json')
console.log(blob);

size: 2
type: "application/json"

  • Blob()构造函数返会一个新的Blob对象,Blob的内容由参数数组中给出的值串联组成。
  • FileReader对象允许Web应用程序异步读取存储在用户计算机上的文件的内容。使用File或者Blob对象指定要读取的文件或数据
    • readAsText()读取文本文件,返回文本字符串,默认编码使utf-8
    • readAsDataURL():读取文本获取一段以data开头的字符串,这段字符串的本质就是DataURL,DataURL是一种将文件嵌入到文档的方案,DataURL使将资源转为base64编码的字符串形式,并将这些内容存储到url中。
  • 一个blob对象可以被读取成多个类型。
  • 构造函数 const blob = new Blob(array,options)
  • array可以是一个由ArrayBuffer,ArrayBufferView,Blob,DomString等对象构成的Array,或者其他类似对象的混合体,它将会被放入Blob,DOMstring会被编写成utf-8
  • options是一个可选的BlobPropertyBag字典,type默认值是’’,他代表了将会被放入到blob中的数组内容的MIME类型。
  • 例子:
const buffer = new ArrayBuffer(2); //设置一个2字节的二进制对象
const blob = new Blob([buffer],  type: "application/json" );
console.log(blob);

//一个blob可以被读成多个类型
function readBlob(blob, type) 
  return new Promise(function (resolve) 
  // fileReader可以读取存储在用户计算机的文件的内容。
    const reader = new FileReader();
    reader.onload = (e) => 
      resolve(e.target?.result);
    ;
    switch (type) 
      case "ArrayBuffer":
        reader.readAsArrayBuffer(blob);
        break;
      case "DataURL": // 二进制数据转为base64
        reader.readAsDataURL(blob);
        break;
      case "Text":
        reader.readAsText(blob, "utf-8");
        break;
      default:
        break;
    
  );

readBlob(blob, "ArrayBuffer").then((res) => 
  console.log(res);
);
readBlob(blob, "DataURL").then((res) => 
  console.log(res);
);
readBlob(blob, "Text").then((res) => 
  console.log(res);
);


//
Blob size: 2, type: 'application/json'
ArrayBuffer(2) // ArrayBuffer类型
data:application/json;base64,AAA= //DataURL类型
空字符串  //Text类型
总结

FIleReader可以读取File或者Blob对象,将其转换成对应的数据,如DataUrl, ArrayBuffer和text等等。
比如input上传的图片是Blob对象,可以通过FileReader转为DataUrl,然后给img标签使用就可以。

// react
 const [imgUrl, setImgUrl] = useState<string>();
 const onInputChnge = (e: React.ChangeEvent<htmlInputElement>) => 
    const file = e.target.files ? e.target.files[0] : null; //input上传的图片是Blob对象
    console.log(file instanceof Blob);
    const fileReader = new FileReader();
    fileReader.readAsDataURL(file!); //转为地址
    fileReader.onload = (e) => 
      console.log(e.target?.result);
      setImgUrl(e.target?.result as string);
    ;
  ;
// html
<input type="file" accept="image/*" onChange=onInputChnge />
<img src=imgUrl alt="" />

Object URL

  • 可以使用浏览器新的API URL对象通过一个方法生成一个地址来表示Blob数据。
  • 格式为 blob:< origin >/< uuid >
  • URL.creatObjectURL静态方法会创建一个DOMString,其中包含一个表示参数中给出的对象的URL,这个URL的生命周期喝创建他的窗口中的document绑定。
  • URL对象表示指定的FIle对象或者Blob对象。
  • revokeObjectURL静态方法用来释放一个值存在的,通过调用URL.createObjectURL()创建的URL对象。
 <button onclick="download()">下载</button>
    <script>

        const debug = name: '123'
        let str = JSON.stringify(debug)

        const blob = new Blob([str],type: 'application/json')
        console.log(blob);
        function download()
             const a = document.createElement('a')
             a.download = 'user.json'
             a.rel = 'noopener'
             a.href = URL.createObjectURL(blob)
             console.log(a);
             //<a download="user.json" rel="noopener" href="blob:null/b0ffdc55-b049-492c-ad92-ac667b27d3ba"></a>
             // 触发a的点击事件
             a.dispatchEvent(new MouseEvent('click'))
             URL.revokeObjectURL(blob) //用于垃圾回收,销毁
        
    </script>

通过Object.createObjectUrl将blob对象转为一个地址,可以看到href返回了一个以blob:开头的地址,
打开浏览器,点击下载之后,下载了一个文件。

user.json
"name":"123"


Blob对象通过Url.createObjectURL创建了url,可以给image使用。

Canvas & DataURl & imageData & Blob/FIle

File类型转为DataUrl,再转为Canvas可以使用的,再截取canvas的数据,通过Canvas.toDataURL()转为DataUrl,最后再转为blob对象,可以上传服务器。

FIle转为DataUrl,借助FileReader
// 通过input事件
  const onInputChnge = (e: React.ChangeEvent<HTMLInputElement>) => 
    setlastPosition( startX: 0, startY: 0 );
    setPosition( startX: 0, startY: 0 );
    const file = e.target.files ? e.target.files[0] : null; //input上传的图片是Blob对象
    console.log(file instanceof Blob);
    const fileReader = new FileReader();
    fileReader.readAsDataURL(file!); //转为地址
    fileReader.onload = (e) => 
      console.log(e.target?.result); // 这就是转化后的url
      setImgUrl(e.target?.result as string);
     
    ;
  ;
//将图片绘制到canvas
 // 将图片绘制到canvas
  const drawImage = (scale1: any = 1, left = 0, top = 0) => 
    if (imgRef.current && canvasRef.current) 
      const image = imgRef.current;
      let canvas = canvasRef.current;
      const ctx = canvas?.getContext("2d");
      // 清掉canvas
      ctx?.clearRect(0, 0, canvas.width, canvas.height);
      //
      let imageWidht = image.width;
      let imageHieght = image.height;
      if (imageWidht > imageHieght) 
        let scale = canvas.width / imageWidht;
        imageWidht = canvas.width;
        imageHieght = imageHieght * scale;
       else 
        let scale = canvas.height / imageHieght;
        imageHieght = canvas.height;
        imageWidht = imageWidht * scale;
      
      requestAnimationFrame(() => 
        ctx?.drawImage(
          image, // Img元素
          (300 - imageWidht) / 2 + left,
          (300 - imageHieght) / 2 + top,
          imageWidht,
          imageHieght
        );
      );
    
  ;

直接通过ctx.drwaImage将元素传进去就行。
canvas数据转为DataUrl

// 截图, canvas数据可以通过toDataURL转为url
  const confirm = (e) => 
    const canvas = canvasRef.current;
    const ctx = canvas?.getContext("2d");
    // 截取,xy坐标+宽高
    const imageData = ctx?.getImageData(100, 100, 100, 100);
    const avatarCanvas = document.createElement("canvas");
    avatarCanvas.width = 100;
    avatarCanvas.height = 100;
    const avatrCtx = avatarCanvas.getContext("2d");
    avatrCtx?.putImageData(imageData!, 0, 0);
    const dataURl = (avatarCanvas as any).toDataURL(); //转为url ,这个是关键
    setImgUrl1(dataURl);
  ;

DataUrl转为Blob对象供上传
通过window.atob转化,然后通过ArrayBuffer和uInt8Array转化。

// 转为blob对象可供上传
  // atob()用于解码 base64编码的字符串
  // btoa()//将字符串编码成base64字符串
  const onUpload = (event) => 
    const bytes: string = window.atob(imgUrl1!.split(",")[1]);
    const arrayBuffer = new ArrayBuffer(bytes.length); //创建一个长度一样大的buffer数组
    const uInt8Array = new Uint8Array(arrayBuffer);
    for (let i = 0; i < bytes.length; i++) 
      uInt8Array[i] = bytes.charCodeAt[i];
    
    const blob = new Blob([arrayBuffer],  type: "image/png" ); // 转为blob对象
    console.log("blob", blob);

	// 通过FileReader又将blob转为DataUrl
    const fileReader = new FileReader();
    fileReader.readAsDataURL(blob!); //转为地址
    fileReader.onload = (e) => 
      console.log(e.target?.result);
      setImgUrl2(e.target?.result as string);
    ;
  ;

这里实现的是一个上传图片并且截图头像的功能。所有代码如下,因demo,所以没有整理,随便写了下:

import React,  useState  from "react";
import ReactDOM from "react-dom";
import "core-js/stable";
import "regenerator-runtime/runtime";
import  useSetRem  from "./utils";
import "./assets/css/init.less";
import  Button  from "antd";

// const buffer = new ArrayBuffer(2); //设置一个2字节的二进制对象
// const blob = new Blob([buffer],  type: "application/json" );
// console.log(blob);

// //一个blob可以被读成多个类型
// function readBlob(blob, type) 
//   return new Promise(function (resolve) 
//     const reader = new FileReader();
//     reader.onload = (e) => 
//       resolve(e.target?.result);
//     ;
//     switch (type) 
//       case "ArrayBuffer":
//         reader.readAsArrayBuffer(blob);
//         break;
//       case "DataURL": // 二进制数据转为base64
//         reader.readAsDataURL(blob);
//         break;
//       case "Text":
//         reader.readAsText(blob, "utf-8");
//         break;
//       default:
//         break;
//     
//   );
// 
// readBlob(blob, "ArrayBuffer").then((res) => 
//   console.log(res);
// );
// readBlob(blob, "DataURL").then((res) => 
//   console.log(res);
// );
// readBlob(blob, "Text").then((res) => 
//   console.log(res);
// );

const App = () => 
  useSetRem();
  const [size, setSize] = useState(1);
  const imgRef = React.useRef<HTMLImageElement>(null);
  const [imgUrl, setImgUrl] = useState<string>();
  const [imgUrl1, setImgUrl1] = useState<string>();
  const [imgUrl2, setImgUrl2] = useState<string>();
  const onInputChnge = (e: React.ChangeEvent<HTMLInputElement>) => 
    setlastPosition( startX: 0, startY: 0 );
    setPosition( startX: 0, startY: 0 );
    const file = e.target.files ? e.target.files[0] : null; //input上传的图片是Blob对象
    console.log(file instanceof Blob);
    const fileReader = new FileReader();
    fileReader.readAsDataURL(file!); //转为地址
    fileReader.onload = (e) => 
      console.log(e.target?.result);
      setImgUrl(e.target?.result as string);
      imgRef.current?.addEventListener("load", () => drawImage(1));
    ;
  ;

  // 将图片绘制到canvas
  const drawImage = (scale1: any = 1, left = 0, top = 0) => 
    console.log(123123);
    if (imgRef.current && canvasRef.current) 
      const image = imgRef.current;
      let canvas = canvasRef.current;
      const ctx = canvas?.getContext("2d");
      // 清掉canvas
      ctx?.clearRect(0, 0, canvas.width, canvas.height);
      //
      let imageWidht = image.width;
      let imageHieght = image.height;
      if (imageWidht > imageHieght) 
        let scale = canvas.width / imageWidht;
        imageWidht = canvas.width;
        imageHieght = imageHieght * scale;
       else 
        let scale = canvas.height / imageHieght;
        imageHieght = canvas.height;
        imageWidht = imageWidht * scale;
      
      imageHieght = imageHieght * Number(scale1);
      imageWidht = imageWidht * Number(scale1);
      console.log("top", top);
      console.log("left", left);

      console.log("imageWidht", imageWidht);

      requestAnimationFrame(() => 
        ctx?.drawImage(
          image,
          (300 - imageWidht) / 2 + left,
          (300 - imageHieght) / 2 + top,
          imageWidht,
          imageHieght
        );
      );
    
  ;

  const canvasRef = React.useRef<HTMLCanvasElement>(null);

  const position = React.useRef( startX: 0, startY: 0 );
  const setPosition = (e) => 
    position.current = e;
  ;
  const lastPosition = React.useRef( startX: 0, startY: 0 );
  const setlastPosition = (e) => 
    lastPosition.current = e;
  ;
  const Drag = React.useRef(false);
  // 按下
  const handleMouseDown = (e) => 
    console.log("lastPosition", lastPosition);
    setPosition(
      startX: e.clientX,
      startY: e.clientY,
    );
    Drag.current = true;
  ;

  // 移动
  const handleMouseMove = (e) => 
    if (Drag.current) 
      // x方向移动的量, y方向移动的量
      drawImage(
        size,
        e.clientX - position.current.startX + lastPosition.current.startX,
        e.clientY - position.current.startY + lastPosition.current.startY
      );
    
  ;

  // 抬起
  const handleMouseUp = (e) => 
    if (Drag.current) 
      setlastPosition(
        startX:
          e.clientX - position.current.startX + lastPosition.current.startX,
        startY:
          e.clientY - position.current.startY + lastPosition.current.startY,
      );
      Drag.current = false;
    
  ;

  // 截图, canvas数据可以通过toDataURL转为url
  const confirm = (e) => 
    const canvas = canvasRef.current;
    const ctx = canvas?.getContext("2d");
    // 截取,xy坐标+宽高
    const imageData = ctx?.getImageData(100, 100, 100, 100);
    const avatarCanvas = document.createElement("canvas");
    avatarCanvas.width = 100;
    avatarCanvas.height = 100;
    const avatrCtx = avatarCanvas.getContext("2d");
    avatrCtx?.putImageData(imageData!, 0, 0);
    const dataURl = (avatarCanvas as any).toDataURL(); //转为url
    setImgUrl1(dataURl);
  ;

  // 转为blob对象可供上传
  // atob()用于解码 base64编码的字符串
  // btoa()//将字符串编码成base64字符串
  const onUpload = (event) => 
    const bytes: string = window.atob(imgUrl1!.split(",")[1]);
    const arrayBuffer = new ArrayBuffer(bytes.length); //创建一个长度一样大的buffer数组
    const uInt8Array = new Uint8Array(arrayBuffer);
    for (let i = 0; i < bytes.length; i++) 
      uInt8Array[i] = bytes.charCodeAt[i];
    
    const blob = new Blob([arrayBuffer],  type: "image/png" ); // 转为blob对象
    console.log("blob", blob);

    const fileReader = new FileReader();
    fileReader.readAsDataURL(blob!); //转为地址
    fileReader.onload = (e) => 
      console.log(e.target?.result);
      setImgUrl2(e.target?.result as string);
    ;
  ;

  return (
    <div style= display: "flex" >
      <div style= flex: 1 >
        <input type="file" accept="image/*" onChange=onInputChnge />
        <img ref=imgRef src=imgUrl alt="" />
      </div>
      <div
        style= flex: 1 
        onMouseDown=handleMouseDown
        onMouseMove=handleMouseMove
        onMouseUp=handleMouseUp
      >
        <div style= position: "relative" >
          <canvas
            ref=canvasRef
            width="300px"
            height="300px"
            style= border: "2px dashed blue" 
          ></canvas>
          <div
            style=
              width: 100,
              height: 100,
              background: "yellow",
              opacity: 0.3,
              position: "absolute",
              top: 100,
              left: 100,
            
          ></div>
        </div>
        <div>
          <Button
            onClick=() => 
              setSize((size * 5) / 4);
              console.log(lastPosition);

              drawImage(
                (size * 5) / 4,
                lastPosition.current.startX,
                lastPosition.current.startY
              );
            
          >
            放大
          </Button>
          <Button
            onClick=() => 
              setSize((size * 3) / 4);
              drawImage(
                (size * 3) / 4,
                lastPosition.current.startX,
                lastPosition.current.startY
              );
            
            style= margin: "0 10px" 
          >
            缩小
          </Button>
          <Button onClick=confirm>截取</Button>
        </div>
      </div>
      <div style= flex: 1 >
        <img src=imgUrl1 alt="" />
        <Button onClick=onUpload>上传</Button>
        <img src=imgUrl2 alt="" />
      </div>
    </div>
  );
;

ReactDOM.render(<App></App>, document.getElementById("root"));

效果:

总结

  • 二进制对象有ArrayBuffer,但他一般不可以直接操作,可以通过TypedArray或者DataView操作。
  • Blob对象一般是用来上传图片的类型,Blob对象可以通过URL.createObjectURL(blob)转为base64编码。
  • FIleReader用来操作Blob对象或者FIle对象,比如上传。可以将File对象转为base64,供img标签使用。
  • canvas可以截取数据并且通过canvas.toDataUrl转为base64。
  • 通过window.btoa()和二进制对象,可以将base64转为Blob对象供上传。

以上是关于前端二进制-实战的主要内容,如果未能解决你的问题,请参考以下文章

uniapp上传图片至服务器,获得在线图片链接预览(实战)

web前端入门到实战:纯CSS瀑布流与JS瀑布流

Hyperledger Fabric 智能合约实战 安装fabric

SpringBoot实战之RabbitMQ

Redis 3.2.4集群实战

前端开发 2018 回顾及 2019 展望