JavaScript 将 img 元素导出为 Base64:canvas.toDataURL 与跨域处理

基础方法:canvas.toDataURL

将页面中已有的 <img> 元素转为 Base64:

function imgToBase64(img, type = 'image/png') {
    const canvas = document.createElement('canvas');
    canvas.width = img.naturalWidth;
    canvas.height = img.naturalHeight;

    const ctx = canvas.getContext('2d');
    ctx.drawImage(img, 0, 0);

    return canvas.toDataURL(type);  // 'data:image/png;base64,...'
}

// 等待图片加载后调用
const img = document.querySelector('img');
img.onload = () => {
    const base64 = imgToBase64(img);
    console.log(base64);
};

// 图片已加载时直接调用
if (img.complete) {
    const base64 = imgToBase64(img);
}

naturalWidth/naturalHeight 是图片的原始尺寸,避免取到 CSS 设置的显示尺寸。

跨域图片:crossOrigin=‘anonymous’

图片来自不同域名时,需要设置 crossOrigin 才能在 canvas 中使用:

async function urlToBase64(url) {
    const img = new Image();
    img.crossOrigin = 'anonymous';
    img.src = url;

    await new Promise((resolve, reject) => {
        img.onload = resolve;
        img.onerror = reject;
    });

    const canvas = document.createElement('canvas');
    canvas.width = img.naturalWidth;
    canvas.height = img.naturalHeight;
    canvas.getContext('2d').drawImage(img, 0, 0);

    return canvas.toDataURL('image/png');
}

服务端必须在响应头中返回 Access-Control-Allow-Origin: *,否则 canvas 被标记为”污染(tainted)“,调用 toDataURL() 会报错:

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

跨域无 CORS 头:fetch + FileReader

当目标服务器没有设置 CORS 头时,可以走服务端代理,或在同源页面中用 fetch 请求(如果存在跨域会被拦截)。另一种方案是在已有权限的上下文(如油猴脚本设置 @grant GM_xmlhttpRequest)中绕过同源限制:

// 同源或有权限的上下文
async function urlToBase64ViaFetch(url) {
    const res = await fetch(url);
    const blob = await res.blob();

    return new Promise((resolve) => {
        const reader = new FileReader();
        reader.onloadend = () => resolve(reader.result);
        reader.readAsDataURL(blob);
    });
}

指定输出格式和质量

canvas.toDataURL('image/png');           // PNG,无损
canvas.toDataURL('image/jpeg', 0.8);     // JPEG,质量 80%
canvas.toDataURL('image/webp', 0.9);     // WebP(非所有浏览器支持)

JPEG 不支持透明通道,带透明背景的图片转 JPEG 后透明部分会变黑,建议用 PNG。