用blob相关知识 处理文件下载和canvas生成国旗头像

2,129 阅读3分钟

先看一下这张图

file.jpg 这张图来自某掘金人的...忘记叫啥了。感谢!

算了,开始结合MDN看这张图里面的内容吧。

基础api

blob

Blob表示一个不可变的、原始数据的类文件对象。可以按二进制或者文本的方式读取。 也可以转化成文件。

使用blob()创建一个blob数据。


/**
 * 
 * @array {bolb、ArrayBuffer,DOMstring...}
 * @options 数组内容的MIME类型。
 */
var blob = new Blob(array,options);

实例的属性


  • size
  • type

实例的方法


  • blob.arrayBuffer()
    • 返回promise对象 其中包含 arrayBuffer的二进制数据。
         // 使用
         let bufferPromise  = blob.arrayBuffer();
         let buffer = await blob.arrayBuffer();
      
  • blob.slice()
    • 可将blob切割成小型的blob。类似数组。
          let Ablob = blob.splice(0,2,'')
          // 返回剪切的部分
      
  • blob.stream()
    • 方法返回一个ReadableStream对象,读取它将返回包含在Blob中的数据。
      let ReadableStream = blob.stream();
    
  • blob.text()
    • 方法返回一个 Promise 对象,包含 blob 中的内容,使用 UTF-8 格式编码。
          var textPromise = blob.text();
          var text = await blob.text();
      

file

file是blob的一个继承。

file提供了文件信息的读取。

  [file].type
  [file].size
  [file].name

file文件来自用户通过input选择的fileList。

或者拖动生成的xx对象(我才拖动上传就是这个api),这个之后在去了解。

file处理的和blob相同。

我们可以使用 FileReaderURL.createObjectURL()XMLHttpRequest.send()对file和blob进行处理。

继承方法

  file.splice(start,end,'')

FileReader

file对象允许web应用异步读取原始数据缓冲区的内容, 使用file和blob对象指定要读取的文件或者数据。

读取blob、file等

  let reader = new FileReader()
  reader.onload = (e)=>{
    console.log(e.target.result)
  }
  reader.readAsDataURL( new Blob() )

上面的代码演示了创建一个 FileReader。 通过readAsDataURL读取指定的内容, 在onload(读取完成后)即可以在target.result中返回base64的内容。

除此之外,它还有一下几种方法。


  reader.readAsText()
  在result中返回字符串形式的内容

  reader.readAsArrayBuffer()
  在result中返回ArrayBuffer数据对象

  reader.readAsBinaryString()
  在result中返回二进制数据

URL.createObjectURL

这个方法来自 URL

它允许浏览器用它指定一个 file 或者 blob 并将其其转化为DOMString。 他和DOM息息相关,因此我们在不在使用他转换完成的这个DOMString之后,我们需要使用 window.URL.revokeObjectURL(url)将其释放,得到最佳的性能效果。

基础应用

文件下载

链接类型的文件下载我们分为以下几个步骤

  • 资源文件转为blob类型
    • 当我们下载一个文件的资源链接时,我们可以使用ajax对这个文件资源的responseType设置为blob。 这样我们得到了一个blob类型的文件。这个文件存在浏览器缓冲区中。
  • 获取到缓冲区的文件地址,配合a标签实现下载。
    • 浏览器提供了URL.createObjectURL()方法,可以将链接地址转化为blob的前端url链接。a标签下载 a标签实现下载的过程,不在本次研究范围内。
  • 当已经实现下载之后我们需要对这个缓冲区中的文件进行释放。
    • window.URL.revokeObjectURL(url)

见如下代码:


<DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>文件下载demo</title>
  <script src="../ajax/index.js"></script>
</head>
<body>

  <div onclick="downloadFiel()">下载</div>
  <script>

    let fileUrl = 'https://bloggers-1304641141.cos.ap-beijing.myqcloud.com/img/1636095726424.jpg';

    const downloadFiel = function (url){
      ajax({
        type:"get",
        reqireUrl: url || fileUrl,
        responseType:"blob",
        success:function (res){

          let a = document.createElement('a');

          //将通过ajax处理返回的blob对像,转化为blob类型的url(DOMString);
          let url = window.URL.createObjectURL(res)

          a.href = url;

          // 裁剪最后一个类型编辑,编辑下载名称
          a.download = '测试.'+fileUrl.split('.').reverse()[0]

          // 触发a标签
          a.click()

          // 释放blob DOMString
          window.URL.revokeObjectURL(url)

        }
      })
    }

  </script>

</body>
</html>

canvas图片处理。

canvas图片处理思路

1、这里我们选择文件,将文件使用URL.createObjectURL()将文件转为blob类型的url进行渲染。

2、我们可以通过new Image()加载这个图片,并且使用canvas对图片进行处理。

3、最后使用canvas.toBlob()将处理后的图片转换为blob文件。

4、将这个blob文件使用URL.createObjectURL()得到blob类型的url下载保存下来。

处理过程

额,逻辑算比较简单的,然后就直接写上全部代码啦,可以赋值以下代码直接看。 有点简陋。你可以优化以下,(●ˇ∀ˇ●)。


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>canvas图片处理</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        .img {
            margin-top: 15px;
            text-align: center;
        }
        .btn{
            display: inline-block;
            width: 100px;
            height: 35px;
            line-height: 35px;
            background-color: #42b983;
            border-radius: 20px;
            text-align: center;
            color: #fff;
            transition: 0.4s;
        }
        .btn:hover{
            cursor: pointer;
            opacity: 0.8;
            transition: 0.4s;
        }
    </style>
</head>
<body>
<div class="img">
    <canvas id="image"></canvas>
</div>
<div class="file">
    <input accept="image/*" id="file" type="file"> <div class="btn" id="save">保 存</div>
</div>
<script>

  window.URL =  window.URL || window.webkitURL ;

  let file = document.getElementById('file');
  let canvas = document.getElementById('image');
  let btn = document.getElementById('save')
  let ctx = canvas.getContext('2d');
  canvas.width = 270;
  canvas.height = 270;
  let img = new Image();

  // 定义国旗星星数组
  let stars = []

  file.onchange = function (e) {
    // 使用createObjectURL 找到对应缓存中的地址字符传
    let url = window.URL.createObjectURL(e.target.files[0])
    img.onload = function () {
      handleDraw();
    }
    img.src = url;
  }

  btn.onclick = function (){
    if(file.files.length<1){
      alert('请选择图片')
      return
    }
    canvas.toBlob(function (e){
      let saveUrl = window.URL.createObjectURL(e)
      let a = document.createElement('a')
      console.log(saveUrl)
      a.href=saveUrl
      a.download = 'save.png'
      console.log(a)
      a.click()
      window.URL.revokeObjectURL(saveUrl)
    })
  }

  // 利用crc设置圆角
  const handleBorderRect = (ctx, x, y, w, h, r, color) => {
    ctx.beginPath();
    // 左上角
    ctx.arc(x + r, y + r, r, Math.PI, 1.5 * Math.PI);
    ctx.moveTo(x + r, y);
    ctx.lineTo(x + w - r, y);
    ctx.lineTo(x + w, y + r);
    // 右上角
    ctx.arc(x + w - r, y + r, r, 1.5 * Math.PI, 2 * Math.PI);
    ctx.lineTo(x + w, y + h - r);
    ctx.lineTo(x + w - r, y + h);
    // 右下角
    ctx.arc(x + w - r, y + h - r, r, 0, 0.5 * Math.PI);
    ctx.lineTo(x + r, y + h);
    ctx.lineTo(x, y + h - r);
    // 左下角
    ctx.arc(x + r, y + h - r, r, 0.5 * Math.PI, Math.PI);
    ctx.lineTo(x, y + r);
    ctx.lineTo(x + r, y);

    ctx.fillStyle = color;
    ctx.fill();
    ctx.closePath();
  };


  class Star {
    constructor(ctx, x, y, size) {
      this.ctx = ctx
      this.ponits = []
      this.color = 'yellow'
      for (let i = 1; i <= 5; i++) {
        let temp = {
          x: Math.cos((18 + i * 72) / 180 * Math.PI) * size + size + x,
          y: -Math.sin((18 + i * 72) / 180 * Math.PI) * size + size + y,
          sx: Math.cos((54 + i * 72) / 180 * Math.PI) * 0.4 * size + size + x,
          sy: -Math.sin((54 + i * 72) / 180 * Math.PI) * 0.4 * size + size + y,
        }
        this.ponits.push(temp);
      }
      stars.push(this)
    }

    draw() {
      let list = this.ponits
      this.ctx.beginPath()
      for (let i = 0; i < list.length; i++) {
        let temp = list[i]
        ctx.lineTo(temp.x, temp.y)
        ctx.lineTo(temp.sx, temp.sy)
      }
      this.ctx.fillStyle = this.color
      this.ctx.fill()
      this.ctx.closePath()
    }
  }

  // 添加五角星
  const drawStart = (ctx) => {
    // 计算圆上点的坐标
    function getXyByRound(h, r) {
      return {
        y: Math.sin(Math.PI * 2 / 360 * h) * r,
        x: Math.cos(Math.PI * 2 / 360 * h) * r
      }
    }
    new Star(ctx, 20, 40, 25)
    let index = 0
    while (index < 4) {
      let temp = getXyByRound(index*27, 60)
      index++
      new Star(ctx, temp.x+20, temp.y+40, 10)
    }

    stars.forEach(star => star.draw())
  }

  // 添加渐变色蒙层
  const drawReact = (ctx, x, y, w, h) => {
    let gradient = ctx.createLinearGradient(x, y, w, h)
    ctx.beginPath()
    gradient.addColorStop(0, 'rgba(255,0,0,.9)')
    gradient.addColorStop(1, 'rgba(255,0,0,0)')
    ctx.fillStyle = gradient
    ctx.fillRect(0, 0, w, h)
    ctx.closePath();
  }

  const handleDraw = () => {
    // 剪裁圆角
    handleBorderRect(ctx, 10, 10, 250, 250, 14, 'orange');
    ctx.clip();
    // 绘制图片
    ctx.drawImage(img, 10, 10, 250, 250);
    // 添加渐变色
    drawReact(ctx, 10, 10, 270, 270);
    // 添加五角星整列
    drawStart(ctx);
  };


</script>
</body>
</html>


测试/排除

经过测试,在手机端微信浏览器中下载图片不可行,在手机内置浏览器中是可以的。

因此在遇到在微信内下载图片的需求时,

可以引导至外部浏览器中进行,或者将图片用a标签包裹,打开此图长按下载。