JS实现图片url、base64、blob几种类型转换和图片截图

13,091 阅读2分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

前言

工作中经常遇到图片的各种转换的需求,比如转成base64用于储存、转成blob上传文件、转成url在页面上回显。

有些方法同样适用于其它一般文件,比如转换后数据传输用、download url用等。

url to base64

用canvas转换。

function url2Base64(url, type = 'image/jpeg') {
    return new Promise((resolve, reject) => {
        const img = new Image()
        const canvas = document.createElement('canvas');
        img.crossOrigin = '*';
        img.onload = function () {
            const width = img.width, height = img.height;
            canvas.width = width;
            canvas.height = height;

            const ctx = canvas.getContext('2d');
            ctx.fillStyle = 'white';
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            ctx.drawImage(img, 0, 0, width, height);
            const base64 = canvas.toDataURL(type);
            resolve(base64);
        };
        img.onerror = function () {
            reject(new Error('message'));
        };
        img.src = url;
    });
}

canvas.toDataURL方法第一个参数可以定义转换类型,默认image/png,如果原图是jpg,转换为png后size会变大。base64的图片实际size可以通过base64.length * 3 / 4来换算成字节长度。

所以最好是可以根据原图类型来定义转换格式,或者直接定义默认为image/jpeg

另外如果原图有透明区域,部分情况转换后会回显为黑色,这时候可以给canvas默认设置底色为白色。

ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvas.width, canvas.height);

blob to base64

function blob2Base64(blob) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.addEventListener('load', () => {
            const base64 = reader.result?.toString() || '';
            resolve(base64);
        });
        reader.addEventListener('error', () => {
            reject(new Error('message'));
        });
        reader.readAsDataURL(blob);
    });
}

也可以尝试用此方式把url转成base64.

调用:

const onChange = async () => {
    const blob = inputRef.current.files[0];
    const base64 = await blob2Base64(blob)
};
<input ref={inputRef} type="file" onChange={onChange} />

url to blob

通过http请求获取blob对象。

function url2Blob(url) {
    return window.fetch(url).then(response => response.blob());
}

blog to url

就一个api URL.createObjectURL,返回值是url string,格式大概是blob:http://localhost:3000/56a79464-d044-4160-8188-a74f58128786blob:开头,后面跟着当前网站域名加一个guid。这个url的生命周期和创建它的窗口中的document绑定,即刷新页面、关闭页面就会被自动释放了,就失效了。可以当做页面图片回显或者download link用,不能用作存储

这种URL形式回显在页面中,会大幅提高性能,尤其是图片非常多的情况。但是要注意释放时机。

developer.mozilla.org/zh-CN/docs/… 这里有详解。

function blob2Url(blob) {
    return URL.createObjectURL(blob);
}

base64 to url

先转成blob,然后再转url。

function base642Url(data) {
    var parts = data.split(';base64,'),
        contentType = parts[0].split(':')[1],
        raw = window.atob(parts[1]),
        length = raw.length,
        arr = new Uint8Array(length);
    for (var i = 0; i < length; i++) {
        arr[i] = raw.charCodeAt(i);
    }
    var blob = new Blob([arr], { type: contentType });
    return URL.createObjectURL(blob);
};

image crop

图片局部截图功能,输出base64。

const TO_RADIANS = Math.PI / 180;
function crop2Blob(image, crop, scale = 1, rotate = 0) {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    if (!ctx) {
        throw new Error('No 2d context');
    }

    const scaleX = image.naturalWidth / image.width;
    const scaleY = image.naturalHeight / image.height;
    const pixelRatio = window.devicePixelRatio || 1

    canvas.width = Math.floor(crop.width * scaleX * pixelRatio);
    canvas.height = Math.floor(crop.height * scaleY * pixelRatio);

    ctx.scale(pixelRatio, pixelRatio);
    ctx.imageSmoothingQuality = 'high';

    const cropX = crop.x * scaleX;
    const cropY = crop.y * scaleY;

    const rotateRads = rotate * TO_RADIANS;
    const centerX = image.naturalWidth / 2;
    const centerY = image.naturalHeight / 2;

    ctx.save();

    ctx.translate(-cropX, -cropY);
    ctx.translate(centerX, centerY);
    ctx.rotate(rotateRads);
    ctx.scale(scale, scale);
    ctx.translate(-centerX, -centerY);
    ctx.drawImage(image, 0, 0, image.naturalWidth, image.naturalHeight, 0, 0, image.naturalWidth, image.naturalHeight);

    ctx.restore();

    const result = canvas.toDataURL('image/jpeg'); // 需要注意输出格式
    return result;
}

参数讲解:

  1. image Image类型对象,也就是img标签的DOM对象。如果没有,
  2. crop 横纵轴坐标及高宽。
  3. scale 缩放,默认不缩放。>1 放大,<1 缩小。
  4. rotate 旋转角度,按中心点顺时针旋转,比如45即顺时针旋转45度。

例子:截取左上角300*300区域图片。

const base64 = await crop2Blob(imgRef.current, {
    width: 300,
    height: 300,
    x: 0,
    y: 0,
});
<img src="..." ref={imgRef} />

如果只有url或base64变量,且当前HTML里没有定义的img DOM,可以手动new一个。需要load之后再传参,不然取不到图片高宽。

Convert url\base64 to Image DOM

function loadImage(src) {
    return new Promise((resolve, reject) => {
        const img = new Image();
        img.src = src;
        img.onload = () => {
            resolve(img);
        };
        img.onError = e => {
            reject(e);
        };
    });
}

最后

通过上面的实现发现,canvas在图片处理中用到的比较多,还可以用来压缩图片、拼接图片等各种操作。

上面的实现可能并不是最优方法,可以在有类似需求时当做参考。