DataURL,Blob和File对象的相互转换

1,811 阅读2分钟

DataURL,Blob和File是常用的前端存放二进制数据的对象,它们用在不同地方,有时候需要三者之间的相互转换

转为DataURL

Blob ==> DataURL

首先最简单的方法是使用FileReader来解析Blob对象:

const blob = new Blob([JSON.stringify({ hello: 'javascript' }, null, 2)], {type : 'application/json'});

const fr = new FileReader();
fr.onload = function (e) {
  console.log(e.target.result); // data:application/json;base64,ewogICJoZWxsbyI6ICJqYXZhc2NyaXB0Igp9
}
fr.readAsDataURL(blob)

当然,你也可以选择将Blob转换为字符串,再使用btoa转换为base64编码的DataURL

需要注意的是,btoa对原始字符串有要求,字符编码必须为Latin-1,Latin-1是单字节编码字符集,也就是每个字符的编码不能超过255,否则会报错

const blob = new Blob([JSON.stringify({ hello: 'javascript' }, null, 2)], {type : 'application/json'});

async function readAsDataURL(blob) {
  const str = await blob.text();
  const dataURL = 'data:' + blob.type + ';base64,' + btoa(str);
  return dataURL;
}

readAsDataURL(blob).then(dataurl => {
  console.log(dataurl); // data:application/json;base64,ewogICJoZWxsbyI6ICJqYXZhc2NyaXB0Igp9
})

ArrayBuffer ==> DataURL

与上面通过Blob转为DataURL类似,也可以通过btoa来将ArrayBuffer转换为DataUrl

const arrBuf = await fetch('test.png').then(res => res.arrayBuffer()); // 假设数据 arrBuf 为 ArrayBuffer

function readAsDataURL(arraybuffer, type) {
    let str = '';
    new Uint8Array(arraybuffer).forEach(code => {
        str += String.fromCodePoint(code);
    });
    return 'data:' + type + ';base64,' + btoa(str);
}

const dataURL = readAsDataURL(arrBuf,  'image/png');

Image ==> DataURL

如果你想转换的是图像,那么还有一种通过canvas间接转换的方法:

有一点需要注意的是,img需要显式地添加img.crossOrigin = 'Anonymous',否则当canvas执行toDataURL(), toBlob(), getImageData()时,由于跨域安全性问题,会抛出错误:

Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.

function imageToDataURL(imgurl) {
    return new Promise((resolve, reject) => {
        const canvas = document.createElement('canvas');
        const img = new Image();
        img.src = imgurl;
        img.crossOrigin = 'Anonymous';
        img.onload = function() {
            canvas.width = img.naturalWidth * window.devicePixelRatio;
            canvas.height = img.naturalHeight * window.devicePixelRatio;
            const ctx = canvas.getContext('2d');
            ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
            const dataURL = canvas.toDataURL();
            resolve(dataURL);
        }
        img.onerror = reject;
    });
}

const dataURL = await imageToDataURL('http://test.com/image.png');

转为Blob

DataURL ==> Blob

如果你的dataurl包含的是image数据,那么同样可以通过canvas进行中转

function dataURLToBlob(dataurl) {
    return new Promise((resolve, reject) => {
        const canvas = document.createElement('canvas');
        const img = new Image();
        img.src = dataurl;
        img.crossOrigin = 'Anonymous';
        img.onload = function() {
            canvas.width = img.naturalWidth * window.devicePixelRatio;
            canvas.height = img.naturalHeight * window.devicePixelRatio;
            const ctx = canvas.getContext('2d');
            ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
            canvas.toBlob(blob => resolve(blob));
        }
        img.onerror = reject;
    });
}

const blob = dataURLToBlob('data:image/png;base64,xxxxxxx');

第二种方法是通过atob函数来转换

function dataURLToBlob(dataurl) {
  const type = dataurl.match(/data:(.+);/)[1];
  const base64 = dataurl.split(',')[1];
  const binStr = atob(base64);
  const u8a = new Uint8Array(binStr.length);
  let p = binStr.length;
  while (p) {
    p--;
    u8a[p] = binStr.codePointAt(p);
  }
  return new Blob([u8a], { type });
}
ArrayBuffer ==> Blob

Blob对象可以直接使用arrayBuffer构建

const blob = new Blob([arrayBuffer], { type: 'image/png' });

转为ArrayBuffer

DataURL ==> ArrayBuffer

只要对dataURL转blob的方法稍作修改

function dataURLToBlob(dataurl) {
  const type = dataurl.match(/data:(.+);/)[1];
  const base64 = dataurl.split(',')[1];
  const binStr = atob(base64);
  const u8a = new Uint8Array(binStr.length);
  let p = binStr.length;
  while (p) {
    p--;
    u8a[p] = binStr.codePointAt(p);
  }
  return u8a.buffer;
}

Blob ==> ArrayBuffer

blob对象有一个arrayBuffer()方法可以直接转换,需要注意,这个方法返回的是Promise对象,其resolve之后的值才是arrayBuffer

const blob = new Blob([1,2,3,4]);
const arrBuf = await blob.arrayBuffer();