【bigo前端】如何截取gif首帧图片

avatar
前端开发工程师 @bigo

file

本文首发于:github.com/bigo-fronte… 欢迎关注、转载。

前言

先介绍一下js文件类型的知识点,后面要多次转换文件类型。

一.什么是Blob、File、DataURL

Blob 类型

Blob 类型是 File 文件类型的父类,它表示一个不可变、原始数据的类文件对象。 如何得到 blob 对象?

new Blob(array, options)
let hiBlob = new Blob([`<h1>Hi gauseen!<h1>`], { type: 'text/html' })

如上代码,就创建了一个 blob 对象,并声明了 text/html 类型 ,就像是创建一个 .html 文件。只不过它存在于浏览器的内存里。

File 类型

File 包含文件的相关信息,可以通过 js 来访问其内容

如何获取 file 对象?

1.new File(bits, name[, options])

// 1. 参数是字符串组成的数组
let hiFile = new File([`<h1>Hi gauseen!<h1>`], 'fileName', { type: 'text/html' })
// 2. blob 转 file 类型
let hiBlob = new Blob([`<h1>Hi gauseen!<h1>`], { type: 'text/html' })
let hiFile = new File([ hiBlob ], 'fileName', { type: 'text/html' })

如上代码,通过 File 构造函数,创建一个 file 对象,与上面的提到的 blob 类似。可以将 blob 转成 file 类型,这意味着上面获取的 blob,可以转成 file 类型。

2.inputElement.files

通过标签获取 file 对象

// input 上传文件时触发 change 事件
$('input').addEventListener('change', e => {
  let file = e.target.files[0]
  console.log('file: ', file)
})

3.DragEvent.dataTransfer.files

通过拖、放获取 file 对象

DataURL(base64)

DataURL,前缀为 data: 协议的 URL,可以存储一些小型数据

语法:data:[][;base64]

如下,黑色 1 像素示例:

data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=

上面提到的 Blob File 类型,如何“消费”它们呢?接着向下看

1.FileReader

允许 Web 应用程序异步读取存储在用户计算机上的文件(blob 或 file)。

// 将 blob 或 file 转成 DataURL(base64) 形式
fileReader(someFile).then(base64 => {
  console.log('base64: ', base64)
})

function fileReader (blob) {
  return new Promise((resolve, reject) => {
    let reader = new FileReader()
    reader.onload = (e) => {
      resolve(e.target.result)
    }
    reader.readAsDataURL(blob)
  })
}

2.convasElement.toDataURL()

可以通过 canvas 图像处理能力,将图片转成 dataURL 形式。在上面 Blob 部分讲解中,代码已实现。

二.DataURL、File、Blob互转

DataURL转Blob对象

// DataURL转Blob对象
export function dataURLToBlob(dataurl, mine = 'image/jpeg'): Blob {
  const arr = dataurl.split(",");
  const mimeType = mine || arr[0].match(/:(.*?);/)[1];
  const bstr = atob(arr[1]);
  let n = bstr.length;
  const u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new Blob([u8arr], { type: mimeType });
}

DataURL转File对象

// DataURL转File对象
function dataURLtoFile(dataurl, filename, mine = 'image/jpeg') {
  const arr = dataurl.split(',');
  const mimeType = mine || arr[0].match(/:(.*?);/)[1];
  const bstr = atob(arr[1])
  let n = bstr.length;
  const u8arr = new Uint8Array(n);
  while(n--){
      u8arr[n] = bstr.charCodeAt(n);
  }
  return new File([u8arr], filename, {type:mimeType});
}

更多转换方式:blog.csdn.net/hahahhahaha…

三.gif截取插件SuperGif

原理:图象标识符(Image Descriptor)

一个GIF文件内可以包含多幅图象,一幅图象结束之后紧接着下是一幅图象的标识符,图象标识符以0x2C('','')字符开始, 定义紧接着它的图象的性质,包括图象相对于逻辑屏幕边界的偏移量、图象大小以及有无局部颜色列表和颜色列表大小, 由10个字节组成

开源插件:github.com/buzzfeed/li…

四.antd upload组件处理

项目框架使用了antd,故需要在上传回调时进行首帧截取。 upload组件回调参数返回File类型

  1. file转换为dataUrl
  2. 新建img实例接收dataUrl数据
  3. 初始化SuperGif实例
  4. 截取首帧图片,返回dataUrl数据
  5. dataUrl转换为file数据
  6. 返回给业务接口,上传图片
export function getGIFFirstFrame(file): Promise<File> {
  return new Promise((resolve) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => {
      const img = new Image();
      // @ts-ignore
      img.src = reader.result;
      img.onload = () => {
        // @ts-ignore
        const rub = new SuperGif({ gif: img });
        rub.load(function () {
          if (rub.get_length() === 0) {
            return;
          }
          // 获取gif实例的首帧
          rub.move_to(0);
          // canvas生成base64图片数据
          const dataurl = rub.get_canvas().toDataURL("image/jpeg", 0.8);
          const filename = file.name.replace('.gif', '.jpg');
          resolve(dataURLtoFile(dataurl, filename));
          return;
        });
      };
    };
  });
  
}

欢迎大家留言讨论,祝工作顺利、生活愉快!

我是bigo前端,下期见。