You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JsPDF结合htmlToImage无法渲染AWS S3图片问题求助

问题场景与代码

我正在开发一个项目,使用htmlToImage插件搭配jsPDF生成发票PDF,核心代码如下:

let invoice = document.getElementById('invoice-detail');
htmlToImage.toPng(invoice)
  .then((dataUrl) => {
    const img = new Image();
    img.src = dataUrl;
    const pdf = new jsPDF('p', 'mm', [210,200]);
    let width = pdf.internal.pageSize.getWidth();
    let height = pdf.internal.pageSize.getHeight();
    pdf.addImage(img, 'PNG', 0, 0, width, height);
    pdf.save('invoice' + '_' + this.invoiceDetail.invoice_no + '.pdf');
  })
  .catch((error) => {
    console.error('oops, something went wrong!', error);
  });

目前遇到的问题是:发票顶部来自AWS S3的图片无法在PDF中稳定渲染,使用Base64格式可正常显示,但在线URL时而有效时而失效。

问题排查与解决方案

1. 图片加载时机未同步

htmlToImage执行转换时,S3图片可能还未完成加载,导致转换后的图片缺失。解决思路是等待目标元素内所有图片加载完成后再执行转换:

// 等待元素内所有图片加载完成的工具函数
function waitForImages(element) {
  const images = element.querySelectorAll('img');
  if (images.length === 0) return Promise.resolve();
  
  const loadPromises = Array.from(images).map(img => {
    if (img.complete) return Promise.resolve();
    return new Promise((resolve, reject) => {
      img.onload = resolve;
      img.onerror = reject;
    });
  });
  
  return Promise.all(loadPromises);
}

// 修改原代码,先等待图片加载
let invoice = document.getElementById('invoice-detail');
waitForImages(invoice)
  .then(() => htmlToImage.toPng(invoice))
  .then((dataUrl) => {
    const img = new Image();
    img.src = dataUrl;
    const pdf = new jsPDF('p', 'mm', [210,200]);
    let width = pdf.internal.pageSize.getWidth();
    let height = pdf.internal.pageSize.getHeight();
    pdf.addImage(img, 'PNG', 0, 0, width, height);
    pdf.save('invoice' + '_' + this.invoiceDetail.invoice_no + '.pdf');
  })
  .catch((error) => {
    console.error('oops, something went wrong!', error);
  });

2. S3资源的CORS或缓存问题

  • CORS配置问题:如果当前页面域名与S3桶域名跨域,未正确配置CORS会导致图片加载失败。需检查S3桶的CORS规则,确保允许当前域名的GET请求,且支持anonymous跨域标识。
  • 缓存问题:浏览器缓存的旧图片资源可能已失效,可给S3图片URL添加时间戳参数避免缓存:
<!-- 在HTML的img标签中添加时间戳 -->
<img src="https://your-s3-bucket-url/logo.png?t={{Date.now()}}" />

3. htmlToImage的跨域图片限制

htmlToImage依赖Canvas API,跨域图片若未配置crossorigin属性,会导致Canvas被污染,无法完成转换。解决方式:

  1. 给S3图片标签添加crossorigin="anonymous"属性:
<img src="https://your-s3-bucket-url/logo.png" crossorigin="anonymous" />
  1. 确保S3桶的CORS规则允许Origin: *或当前域名,且返回Access-Control-Allow-Credentials: false(因为使用了anonymous)。

4. 网络波动导致加载超时

网络不稳定时,S3图片可能加载超时。可提前将S3图片转为Base64后再执行转换,确保图片资源本地可用:

// 将在线图片转为Base64的工具函数
function convertImageToBase64(url) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.crossOrigin = 'anonymous';
    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);
      resolve(canvas.toDataURL('image/png'));
    };
    img.onerror = reject;
    // 设置5秒超时
    setTimeout(() => reject(new Error('图片加载超时')), 5000);
    img.src = url;
  });
}

// 修改原代码,先替换S3图片为Base64
let invoice = document.getElementById('invoice-detail');
const s3Images = invoice.querySelectorAll('img[src^="https://your-s3-bucket-url"]');

Promise.all(Array.from(s3Images).map(img => 
  convertImageToBase64(img.src).then(base64 => {
    img.src = base64;
  })
))
.then(() => htmlToImage.toPng(invoice))
.then((dataUrl) => {
  const img = new Image();
  img.src = dataUrl;
  const pdf = new jsPDF('p', 'mm', [210,200]);
  let width = pdf.internal.pageSize.getWidth();
  let height = pdf.internal.pageSize.getHeight();
  pdf.addImage(img, 'PNG', 0, 0, width, height);
  pdf.save('invoice' + '_' + this.invoiceDetail.invoice_no + '.pdf');
})
.catch((error) => {
  console.error('oops, something went wrong!', error);
});

内容的提问来源于stack exchange,提问作者Rishi Ghosh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 19:25:23