Skip to content

前端二进制(arrayBuffer/Blob/File/fileReader/canvas/base64)

js一张图搞定arrayBuffer/Blob/File/fileReader/canvas/base64的各种转换操作,以及文件上传

二进制

  • ArrayBuffer
  • TypedArray
  • DataView
  • 字符&&base64 转化
  • FileReader && Blob/File 转化
  • File && Blob 转换
  • Bolb
  • DataURL(base64) && OjectURL 用法
  • canvas 上传,综合应用
    • imageData => canvas 应用
    • canvas => imageData 应用
  • express 图片上传服务

图片涉及了流的各种转化 下面是各种例子 可根据需要到下面找对应的例子

file.jpg

二进制的转化

  • 整数部分:除2取余,直到商为0,最先得到的余数是最低位,最后的余数是高位
  • 小数部分:乘2取整,直到积为0或者精确度要求为止,最先得到的整数是高位
  • (3.5)10 = (11.1)2
  • 原码:二进制数,3个bit能表示8个数 +0+1+2+3-0-1-2-3,4正4负(最高位1即为负数,0为正数)
  • 反码:正数不变,负数的除符号外其他位数取反
  • 补码:正数不变,负数在反码的基础上加1(最高位要舍掉)
  • 计算机 只有补码 一种,正数+他负数的补码=0,计算机中通过补码进行计算
  // 负数的补码 + 正数的补码 = 0

  // 负数              正数
  // 原码  1 001011    0 001011
  // 反码  1 110100    1 110101
  // 补码  1 110101

  // 4     +   (-4)  = 0
  // 00100 +   10100 
  // 00100 +   11011(反码)
  // 00100 +   11100(补码)
复制代码

ArrayBuffer(最基本的)

  • 8位 == 1个字节,ArrayBuffer字节数组(每个都是8位),他里面存放的就是字节
// 8个字节*8个位 = 64位
let buffer = new ArrayBuffer(8)
复制代码

TypedArray 类型数据

let buffer = new ArrayBuffer(8);// 8个字节的ArrayBuffer
const int8Array = new Int8Array(buffer)// 1个整数占据8个位 
console.log(int8Array.length)//  元素的长度8,1个字节占据8个位

console.log(int8Array.buffer === buffer)// true

const int16Array = new Int16Array(buffer)// 1个整数占据16个位 
console.log(int16Array.length)// 元素的长度4,1个字节占据16个位
复制代码

DataView 对象

  • DataView 视图是一个可以从二进制ArrayBuffer对象中读写多种数值类型的底层接口
  • setInt8()从DataView起始位置以byte为计数的指定偏移量(byteOffset)处储存一个8-bit数(一个字节)
  • getInt8()从DataView起始位置以byte为计数的指定偏移量(byteOffset)处获取一个8-bit数(一个字节)
  • DataView和TypeArray 内部都引用了buffer,buffer不能直接用

DataView.jpg

const buffer = new ArrayBuffer(8);
// 创建一个8字节的 arrayBuffer
console.log(buffer.byteLength);//8个字节
const view1 = new DataView(buffer);
// 第一个 整数为8个字节 值为1
view1.setInt8(0, 1); // 0 是开始位子 存入1
console.log(view1.getInt8(0));//1
// 第二个 整数为8个字节 值为1
view1.setInt8(1, 2); // 1 是开始位子 跳过8个字节 存入2
// 读取第二个字节的时候  二进制位 0000 0010 为2
console.log(view1.getInt8(1));//2

// 读取 16位的时候 把图上面的2个二进制拼接上去  得到的是258
console.log(view1.getInt16(0));//258

console.log(view1.buffer === buffer)
复制代码

字符&&base64 转化

  • window.btoa && window.atob
    • 它存在window上
    • btoa 阔以将 字符串转化成一个base64
    • atob 将base64 转化成 字符串
let rs =  btoa('xxx')
// rs 是一个base64字符串

let s = atob(rs)
// atob 把base64转成字符串 s==='xxx'
复制代码

FileReader && Blob/File 转化

  • FileReader函数, 读取Blob,他有3个读取的方式,分别生成ArrayBuffer,base64字符串,文本
  • readAsArrayBuffer生成的是 ArrayBuffer
  • readAsDataURL生成的是 base64字符串
  • readAsText生成的是 生成的是一个文本
function readBlob(blob,type){
  return new Promise((resolve)=>{
    let reader = new FileReader()
    reader.onload = function(event){
      resolve(event.target.result)
    }
    switch(type){
      case 'ArrayBuffer':
      // readAsArrayBuffer生成的是 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(rs=>{
  console.log('ArrayBuffer',rs)
})
readBlob(blob,'DataURL').then(rs=>{
  console.log('DataURL',rs)
})
readBlob(blob,'Text').then(rs=>{
  console.log('Text',rs)
})
复制代码

File && Blob 转换

Blob=>File

  • 用法
var file = new File([blob], "foo.png", {
  type: "image/png",
});
复制代码

Blob=>File

let blob = new Blob([file],{
    type:'image/png'
})
复制代码

Blob

  • 文件传输,上传下载,都是 Blob格式
  • Blob函数
  • 构造函数 var aBlob = new Blob( array, options );
    • array 是一个由ArrayBuffer, ArrayBufferView, Blob, DOMString(js string) 等对象构成的 Array ,或者其他类似对象的混合体,它将会被放进 Blob。DOMStrings会被编码为UTF-8。
    • options 是一个可选的BlobPropertyBag字典
    • type 默认值为 "",它代表了将会被放入到blob中的数组内容的MIME类型

OjectURL

  • URL.createObjectURL
    • 可以获取当前文件的一个内存URL
    • 利用 DOMString 生成一个 blob 文件下载成json
// DOMString
function download(){
    let data = 'xxxxx'
    let blob = new Blob([data],{type:'application/json'})
    let a = document.createElement('a')
      a.download = 'user.json'//下载名
      a.rel = 'noopener'
      a.href = URL.createObjectURL(blob)
      a.dispatchEvent(new MouseEvent('click'));
      URL.revokeObjectURL(blob);//销毁objectURL 也会销毁Blob
    console.log(blob,URL.createObjectURL(blob))
  }
复制代码
  • 利用上传图片下载
<input type="file" onchange="handleChange(event)">
<button onclick="download()">下载</button>

<script>
  function download(data){
  let bytes = new ArrayBuffer(data.length)
  let arr = new Uint8Array(bytes)
  for(let i=0;i<data.length;i++){
    arr[i] = data.charCodeAt(i)
  }
  let blob = new Blob([arr],{type:'image/png'})
  let a = document.createElement('a')
    a.download = 'user.png'//下载名
    a.rel = 'noopener'
    // a.href = blob;
    a.href = URL.createObjectURL(blob)
    a.dispatchEvent(new MouseEvent('click'));
    URL.revokeObjectURL(blob);//销毁objectURL 也会销毁Blob
}
  function handleChange(e){
    let file = null;
    file = e.target.files[0];
    let fileReader = new FileReader()
      fileReader.onload = e =>{
        // atob 是一个全局方法
        // base64 必须要转换成 字符数 
        let bytes = atob(e.target.result.split(',')[1])
        download(bytes)
      }
      fileReader.readAsDataURL(file)
  }
</script>
复制代码

DataURL

  • base64 字符串

Blob生成DataURL

  • DataURL阔以直接给img.src 使用
function readBlob(blob,type){
  return new Promise((resolve)=>{
    let reader = new FileReader()
    reader.onload = function(event){
      resolve(event.target.result)
    }
    switch(type){
      case 'ArrayBuffer':
      // readAsArrayBuffer生成的是 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(rs=>{
  console.log('ArrayBuffer',rs)
})
readBlob(blob,'DataURL').then(rs=>{
  console.log('DataURL',rs)
})
readBlob(blob,'Text').then(rs=>{
  console.log('Text',rs)
})
复制代码

input 上传的文件(图片,xlsx等)如何转换成 array

  • FileReader函数,他有3个读取的方式,分别生成ArrayBuffer,base64字符串,文本
  • input 上传文件就是一个 Blob
function readBlob(blob,type){
  return new Promise((resolve)=>{
    let reader = new FileReader()
    reader.onload = function(event){
      resolve(event.target.result)
    }
    switch(type){
      case 'ArrayBuffer':
      // readAsArrayBuffer生成的是 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(rs=>{
  console.log('ArrayBuffer',rs)
})
readBlob(blob,'DataURL').then(rs=>{
  console.log('DataURL',rs)
})
readBlob(blob,'Text').then(rs=>{
  console.log('Text',rs)
})
复制代码
  • basr64 => ArrayBuffer => blob,下载图片
  <input type="file" onchange="handleChange(event)">
  <button onclick="download()">下载</button>


  <script>
    function download(data){
    let bytes = new ArrayBuffer(data.length)
    let arr = new Uint8Array(bytes)
    for(let i=0;i<data.length;i++){
      arr[i] = data.charCodeAt(i)
    }
    let blob = new Blob([arr],{type:'image/png'})
    let a = document.createElement('a')
      a.download = 'user.png'//下载名
      a.rel = 'noopener'
      // a.href = blob;
      a.href = URL.createObjectURL(blob)
      a.dispatchEvent(new MouseEvent('click'));
      URL.revokeObjectURL(blob);//销毁objectURL 也会销毁Blob
  }
    function handleChange(e){
      let file = null;
      file = e.target.files[0];
      let fileReader = new FileReader()
        fileReader.onload = e =>{
          // atob 是一个全局方法
          // base64 必须要转换成 字符数 
          let bytes = atob(e.target.result.split(',')[1])
          download(bytes)
        }
        fileReader.readAsDataURL(file)
    }
  </script>
复制代码

Blob 转换 base64或者image

  • URL.createObjectURL
  • reader.readAsDataURL
  <input type="file" onchange="handleChange(event)">
  <img src='' id='img'>
  <script>
  function handleChange(e){
    let file = null;
    file = e.target.files[0];
    let url = URL.createObjectURL(file)
    img.src=  url
  }

  function handleChange(e){
    let file = null;
    file = e.target.files[0];
    let fileReader = new FileReader()
      fileReader.onload = e =>{
        img.src = e.target.result
        console.log(img.src)
      }
      fileReader.readAsDataURL(file)
  }
  </script>
复制代码

Canvas

imageData => canvas 应用

canvas => imageData 应用

  • 图片截图上传 对照上图的api进行
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    body{
      display: flex;
    }
    .div1 img{
      border: 2px dashed green;
      width: 500px;
    }
    .div1{
      display: flex;
      width: 500px;
      flex-wrap: wrap;
    }
  </style>
</head>
<body>
    <div class="div1">
        <input type="file" accept="image/*" onchange="handleChange(event)">
        <img src="" alt="" id='img'>
    </div>

    <style>
      .div2{
        margin-left: 50px;
        position: relative;
      }
      #can{
        border: 2px dashed blue;
      }
      .divCenter{
        position: absolute;
        width: 100px;
        height: 100px;
        background: yellow;
        opacity: 0.3;
        left: 0;
        right: 0;
        top: 0;
        bottom: 0;
        margin: auto;
      }
    </style>

    <div class="div2" onmousedown="handleMouseDown(event)" onmousemove="handleMouseMove(event)" onmouseup="handleMouseUp(event)">
      <canvas
        id="can"
        width="300px"
        height="300px"
      ></canvas>
      <div class="divCenter"></div>
      <div class="btn-group">
        <button type="button" onclick="bigger()">变大</button>
        <button type="button" onclick="smaller()">变小</button>
        <button type="button" onclick="confirm()">剪辑</button>
      </div>
    </div>

    <div class="div3">
      <img width="100px" src="" alt="" id='img1'>
      <button onclick="upload()">上传</button>
    </div>
    <script>
      let state = {
        timer:1,// 放大倍数
        startX:0,// 鼠标按下的开始X坐标
        startY:0,
        startDrag:false,//开始拖动
        lastX:0,// 上一次鼠标抬起的位子
        lastY:0,
        avatarDataURL:''
      }
      let img = document.querySelector('#img')
      let img1 = document.querySelector('#img1')
      let can = document.querySelector('canvas')
      upload = ()=>{
        // base64格式:类型+,+数据 
        // atob 转换的时候只能对base64的数据进行处理
        let bytes = atob(state.avatarDataURL.split(',')[1])

        let arrayBuffer = new ArrayBuffer(bytes.length)
        let uint8Array = new Uint8Array(arrayBuffer);
        for(let i=0;i<bytes.length;i++){
          uint8Array[i] = bytes.charCodeAt(i)
        }
        let blob = new Blob([uint8Array],{type:'image/png',ss:'xxx'});
        let xhr = new XMLHttpRequest
        let formData = new FormData();
        formData.append('avatar',blob)
        xhr.open('POST','http://localhost:4000/upload',true);
        xhr.send(formData);
      }
      
      bigger = ()=>{
        state.timer +=0.1 
        drawImage()
      }
      smaller = ()=>{
        state.timer -=0.1 
        drawImage()
      }
      confirm = (event)=>{
        let ctx = can.getContext('2d')
        // 复制画布上指定矩形的像素数据
        // canvas => imageData
        const imageData = ctx.getImageData(100,100,100,100)
        let avatar =  document.createElement('canvas')
        avatar.width =100
        avatar.height= 100
        let avatarCtx = avatar.getContext('2d');
        // 然后通过 putImageData() 将图像数据放回画布
        // imageData => canvas
        avatarCtx.putImageData(imageData,0,0)
        // base64
        let avatarDataURL = avatar.toDataURL()
        state.avatarDataURL = avatarDataURL
        img1.src = avatarDataURL
      }
      handleMouseDown = (event)=>{
        state.startX = event.clientX
        state.startY = event.clientY
        state.startDrag = true
      }
      handleMouseMove = (event)=>{
        // X仿效移动的量 Y方向移动的量
        if(state.startDrag)
        drawImage(
          event.clientX-state.startX+state.lastX,
          event.clientY-state.startY+state.lastY  )
      }
      handleMouseUp = (event)=>{
        state.startDrag = false
        state.lastX = event.clientX - state.startX +state.lastX
        state.lastY = event.clientY - state.startY +state.lastY
      }
      drawImage = (left=state.lastX,top=state.lastY)=>{
        let ctx = can.getContext('2d')
        ctx.clearRect(0,0,can.width,can.height)
        let imageWidth = img.width
        let imageHeight = img.height
        if(imageWidth>imageHeight){// 如果宽度比高度达 图片的宽度调整为canvas宽度
          let scale = can.width/imageWidth
          imageWidth = can.width*state.timer
          imageHeight = can.height*scale*state.timer
        }else{
          let scale = can.height/imageHeight
          imageHeight = can.height*state.timer
          imageWidth = can.width*scale*state.timer
        }
        // 画图片
        ctx.drawImage(img,(can.width-imageWidth)/2+left,(can.height-imageHeight)/2+top,imageWidth,imageHeight)
      }

      function handleChange(e){
        let file = e.target.files[0];
        let fileReader = new FileReader()
        fileReader.onload = e =>{
          img.src = e.target.result
          // 图片加载成功之后 执行回调 绘制canvas
          img.onload=()=>drawImage()
        }
        fileReader.readAsDataURL(file)
      }
    </script>
</body>
</html>
复制代码

后台

  • express
// 图片上传
let express = require('express');
let multer = require('multer')
let fs = require('fs')
let cors = require('cors');
let path = require('path')
let app = express();
// 处理json格式的请求体
app.use(express.json());
// 处理表单格式的请求体
app.use(express.urlencoded({ extended: true }));
app.use(cors());
// 文件上传 upload req.file获取文件的流   dest req.file 获取的保存路径
let upload = multer({ upload: 'upload/' })

app.post('/post', (req, res) => {
  let body = req.body
  console.log(body)
  res.send(body)
})
app.post('/form', (req, res) => {
  let body = req.body
  res.send(body)
})
// 只有一个文件类型的用upload.single('avatar') 处理
app.post('/upload', upload.single('avatar'), (req, res) => {
  // req.file 里面存放的文件类型的数据
  // req.body 里面存放的普通类型的数据
  if (req.file) {
    let rs = '.'+req.file.mimetype.match(/.+\/(.+)/)[1]
	console.log('rs', req.file);
	console.log('req.file.originalname', req.file.originalname);
    fs.writeFileSync(path.join(__dirname, `/${req.file.fieldname}${rs}`), req.file.buffer)
  }
  res.send(req.body)
})

// xls
app.post('/xls', upload.single('avatar'), (req, res) => {
  if (req.file) {
    fs.writeFileSync(path.join(__dirname, `/${req.file.fieldname}.xls`), req.file.buffer)
  }
  res.send(req.body)
})

app.listen(4000)

Released under the MIT License.