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

JavaScript如何同步从URL图片获取DataURI?解决PDF图片错位问题

解决jsPDF异步加载图片导致位置错误的问题

你遇到的核心问题其实不是“如何同步获取DataURI”——浏览器出于性能和安全限制,根本不支持同步XHR获取Blob类型的响应,所以你没法用同步方式实现。真正要解决的是异步操作的时序控制:你的PDF页面布局逻辑是同步执行的,但图片加载是异步的,导致图片加载完成时,PDF已经生成了错误的页面结构,最终图片位置错乱。

下面给你两种可靠的解决方案,都是通过理顺异步流程来保证图片和PDF布局的同步:


方案一:先批量加载所有图片,再统一生成PDF

这种方式会先把所有需要的图片转换成DataURI并缓存,等全部加载完成后,再执行PDF的布局和图片添加逻辑,完全避免异步时序问题。

第一步:封装Promise版的图片转DataURI函数

function getImageDataUri(url) {
  return new Promise((resolve, reject) => {
    const request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.responseType = 'blob';
    
    request.onload = function() {
      if (request.status === 200) {
        const reader = new FileReader();
        reader.onload = function(e) {
          // 替换MIME类型(根据你的需求调整)
          const imgData = e.target.result.replace("data:application/xml;", "data:application/png;");
          resolve(imgData);
        };
        reader.onerror = () => reject(new Error('FileReader读取失败'));
        reader.readAsDataURL(request.response);
      } else {
        reject(new Error(`图片加载失败:${url},状态码:${request.status}`));
      }
    };
    
    request.onerror = () => reject(new Error('XHR请求失败'));
    request.send();
  });
}

// 或者用更简洁的fetch替代XHR:
// async function getImageDataUri(url) {
//   const response = await fetch(url);
//   if (!response.ok) throw new Error(`图片加载失败:${url}`);
//   const blob = await response.blob();
//   return new Promise((resolve, reject) => {
//     const reader = new FileReader();
//     reader.onload = e => resolve(e.target.result.replace("data:application/xml;", "data:application/png;"));
//     reader.onerror = reject;
//     reader.readAsDataURL(blob);
//   });
// }

第二步:修改PDF生成逻辑,先加载所有图片再处理

async function generatePDF() {
  const doc = new jsPDF();
  // 初始化你的PDF状态变量
  let g_iPDFGpage = 0;
  let g_iPDFGimg = 0;
  let g_fPDFGlastPos = 0;
  let g_bPDFGisLeft = false;

  // 1. 预收集所有图片的布局信息和加载任务
  const imageJobs = [];
  $('#imgArea img').each(function() {
    g_bPDFGisLeft = !g_bPDFGisLeft;
    let bPDFGposCheck = true;
    let pos;

    // 计算当前图片的页面位置
    while(bPDFGposCheck) {
      pos = (g_iPDFGpage === 0) 
        ? doc.autoTable.previous.finalY + 8 + (75 * g_iPDFGimg) 
        : (75 * g_iPDFGimg);
      
      if((pos + (75 * (g_iPDFGimg + 1))) >= 300) {
        doc.addPage();
        g_iPDFGimg = 0;
        g_iPDFGpage++;
        bPDFGposCheck = true;
      } else {
        bPDFGposCheck = false;
      }
    }

    // 保存当前图片的src和布局参数
    imageJobs.push({
      src: $(this).attr('src'),
      pos: pos,
      lastpos: g_fPDFGlastPos,
      isleft: g_bPDFGisLeft
    });

    g_fPDFGlastPos = pos;
    if(!g_bPDFGisLeft) g_iPDFGimg++;
  });

  // 2. 等待所有图片加载完成,拿到对应的DataURI
  const loadedImages = await Promise.all(
    imageJobs.map(job => 
      getImageDataUri(job.src).then(dataUri => ({...job, dataUri}))
    )
  );

  // 3. 统一把图片添加到PDF的正确位置
  loadedImages.forEach(job => {
    const { dataUri, pos, lastpos, isleft } = job;
    doc.addImage(dataUri, 'PNG', isleft ? 15 : 90, isleft ? pos : lastpos, 75, 75);
  });

  // 导出PDF
  doc.save('你的文档名称.pdf');
}

// 调用生成函数
generatePDF();

方案二:串行处理图片,加载一个添加一个

如果图片数量较多,不想等待全部加载完再处理,可以用async/await串行遍历图片,每加载完一张就立即添加到PDF,再计算下一张的位置:

async function generatePDF() {
  const doc = new jsPDF();
  let g_iPDFGpage = 0;
  let g_iPDFGimg = 0;
  let g_fPDFGlastPos = 0;
  let g_bPDFGisLeft = false;

  const $images = $('#imgArea img');
  // 串行遍历每张图片
  for (let i = 0; i < $images.length; i++) {
    const $img = $($images[i]);
    g_bPDFGisLeft = !g_bPDFGisLeft;
    let bPDFGposCheck = true;
    let pos;

    // 计算当前图片的页面位置
    while(bPDFGposCheck) {
      pos = (g_iPDFGpage === 0) 
        ? doc.autoTable.previous.finalY + 8 + (75 * g_iPDFGimg) 
        : (75 * g_iPDFGimg);
      
      if((pos + (75 * (g_iPDFGimg + 1))) >= 300) {
        doc.addPage();
        g_iPDFGimg = 0;
        g_iPDFGpage++;
        bPDFGposCheck = true;
      } else {
        bPDFGposCheck = false;
      }
    }

    // 等待当前图片加载完成
    const imgData = await getImageDataUri($img.attr('src'));
    // 立即添加到PDF的正确位置
    doc.addImage(imgData, 'PNG', g_bPDFGisLeft ? 15 : 90, g_bPDFGisLeft ? pos : g_fPDFGlastPos, 75, 75);

    // 更新状态变量
    g_fPDFGlastPos = pos;
    if(!g_bPDFGisLeft) g_iPDFGimg++;
  }

  doc.save('你的文档名称.pdf');
}

generatePDF();

重要注意事项

  • CORS跨域问题:如果图片所在的服务器域名和你的页面域名不同,必须确保服务器配置了正确的CORS响应头(比如Access-Control-Allow-Origin),否则XHR/fetch请求会被浏览器拦截。
  • 错误处理:上述代码可以根据需求添加try/catch块,处理图片加载失败的情况,比如跳过错误图片或者提示用户。

内容的提问来源于stack exchange,提问作者Gabriel I.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:52:06