灏天阁

JS将图片URL转base64

· Yin灏

背景介绍

最近有个需求是将部分 DOM 生成图片上传到服务器,这里就直接用之前项目使用的 html-to-image

然而,这次与上次不同的是有一个图片;其实,html-to-image 也支持了存在图片的 DOM 生成截图(embed-images)。

出现意外

不出意外的就该出意外了:

企业微信截图_2d1b7e6f-f5d6-4dfe-94d2-a2e7312b6660.png

很容易理解,就是跨域了请求了。注意,这里本来之前使用 img 标签是能正常请求的,并且也不用加 crossorigin 属性。 在调用 html-to-image 中加上 mode: ’no-cors’依然不行。

进入正题吧

然后就想自己搞转 base64 吧,各种百度谷歌出来了

const image2Base64 = (url: string) =>
  new Promise((resolve, reject) => {
    if (!url) {
      resolve("");
      return;
    }
    const img = new Image();
    img.crossOrigin = "anonymous";

    img.src = url;
    img.onload = () => {
      const canvas = document.createElement("canvas");
      canvas.width = img.width;
      canvas.height = img.height;
      const ctx = canvas.getContext("2d");
      ctx?.drawImage(img, 0, 0);
      const data = canvas.toDataURL();

      resolve(data);
    };
    img.onerror = () => {
      reject("");
    };
  });

特别需要注意的是:img 标签属性是 crossorigin,new Image 需使用 crossOrigin。 如果不设置 crossOrigin 会造成另一个问题:

canvas.png

在用 canvas 调用 toDataURL 方法中出错了。

然后又是各种百度谷歌,发现 MDN 有个权威又无语的解释:

mdn.png

提炼一下哈:指定 crossorigin 的图像,在 canvas 调用中不会出现 tainted 错误。

其实上面已经能解决大多数的问题了:

base641.png

对,没猜错,事情没有绝对的,还是有个例的:

base64_2.png

不知是这个图片服务器咋设置的,各种吧啦吧啦沟通也不给设置跨域白名单啥的,只能自己想办法了。

nodejs 中间层转

const http = require("http");
http.get(url, (res) => {
  const chunks = [];
  let size = 0;

  res.on("data", (chunk) => {
    chunks.push(chunk);
    size += chunk.length;
  });

  res.on("end", () => {
    const data = Buffer.concat(chunks, size);
    const base64Data = data.toString("base64");
    return base64Data;
  });
});

结果,完美解决。

总结

  • 使用 crossOrigin 能解决大多数情况
  • 如果能在图片服务器加跨域白名单最好
  • 终极大招就是 nodejs 转
  • nodejs 弊端:对于图片无法使用 CDN,对服务器压力增大,慎用

- Book Lists -