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

Google Apps Script发送单份PDF邮件为何触发urlfetch调用超限错误

问题描述

在Google Apps Script环境中实现将导出的PDF作为邮件附件发送的功能时,触发错误提示:Service invoked too many times for one day: urlfetch.。对代码各执行环节添加日志排查后,所有参数、逻辑校验结果均显示正常,无法定位报错触发点。

问题相关代码
function emailSavePo(poNumber, supplier, unit) {
  poNumber = '2B'
  supplier = 'ABC'
  unit = 'UnitA'
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var spreadsheetId = ss.getId();
  var email = Session.getActiveUser().getEmail();
  var pdfName = 'PO' + ' ' + poNumber + ' - ' + supplier + ' - ' + unit;
  var sheetId = ss.getSheetByName('PO Sheet').getSheetId();
  
  var url_base = ss.getUrl().replace(/edit$/, '');
  var url_ext = 'export?exportFormat=pdf&format=pdf'   //export as pdf
    + (sheetId ? ('&gid=' + sheetId) : ('&id=' + spreadsheetId))
    // 以下为可选导出参数
    + '&size=A4'      // 纸张尺寸
    + '&portrait=true'    // 排版方向,false为横向
    + '&fitw=true'        // 适配宽度,false为实际尺寸
    + '&top_margin=0.50'
    + '&bottom_margin=0.50'
    + '&left_margin=0.50'
    + '&right_margin=0.50'
    + '&sheetnames=true&printtitle=false&pagenumbers=true'  // 页眉页脚显示配置
    + '&gridlines=false'  // 隐藏网格线
    + '&fzr=false';       // 每页不重复冻结行表头

  var options = {
    headers: {
      'Authorization': 'Bearer ' + ScriptApp.getOAuthToken(),
    }
  }
  var response = UrlFetchApp.fetch(url_base + url_ext, options);
  var blob = response.getBlob().setName(pdfName + '.pdf');
  if (email) {
    const mailOptions = {
      attachments: blob
    }
    MailApp.sendEmail(
      email,
      subject + " " + pdfName + "",
      "Hello! \n\nHere is a copy of the PO! \n\nBest regards,\nTeam",
      mailOptions);
    ss.toast('A pdf copy of the PO has been sent to ' + email + '!')
  }
}
原因定位与解决方案

报错核心本质

Service invoked too many times for one day: urlfetch. 是Google Apps Script的配额超限报错,代表当前账号当日UrlFetch服务的调用次数已经达到上限,和单次代码的参数、逻辑校验结果无关,不需要逐行排查代码语法问题。
普通免费Google账号UrlFetch日调用配额为20000次,Google Workspace账号根据版本不同配额在50000-100000次区间,配额会在太平洋时间每日零点自动重置。

代码中导致配额快速耗尽的隐患

  • 函数开头硬编码了测试参数poNumber = '2B'、supplier = 'ABC'、unit = 'UnitA',如果该函数被绑定到onEdit/onChange/表单提交这类高频触发器,表格每次编辑、每次表单提交都会后台静默执行一次UrlFetch请求,不需要手动点击功能就会持续消耗配额。
  • 代码中存在未定义变量subject,且邮件附件参数attachments直接传入单个blob不符合API要求(要求传入blob数组),会导致邮件发送环节抛错,用户遇到报错往往会反复点击触发按钮重试,短时间多次调用会加速配额消耗。
  • 没有做重复调用拦截,同一份PO重复导出时每次都会发起新的UrlFetch请求,没有复用已生成的PDF资源。

修复步骤

  • 删除函数开头三行硬编码的测试参数赋值,参数仅通过功能入口传入,避免触发器无意义触发调用。
  • 修复代码中的显性bug:
    • 提前定义邮件主题变量,例如const subject = "PO Document:"
    • 修正邮件附件参数格式,将attachments: blob改为attachments: [blob]
  • 增加缓存逻辑,同一份PO重复导出时直接读取缓存结果,不重复发起UrlFetch请求,参考实现:
// 生成唯一缓存key
const cacheKey = `po_pdf_${poNumber}_${supplier}_${unit}`;
const cache = CacheService.getScriptCache();
let blob;
const cachedPdf = cache.get(cacheKey);
if (cachedPdf) {
  // 缓存命中直接转换为blob使用
  blob = Utilities.newBlob(Utilities.base64Decode(cachedPdf), 'application/pdf', `${pdfName}.pdf`);
} else {
  // 缓存未命中才发起导出请求
  const response = UrlFetchApp.fetch(url_base + url_ext, options);
  blob = response.getBlob().setName(pdfName + '.pdf');
  // 结果写入缓存,有效期可按需调整,最大支持21600秒(6小时)
  cache.put(cacheKey, Utilities.base64Encode(blob.getBytes()), 21600);
}
  • 检查当前脚本下所有已绑定的触发器,删除非必要的高频自动触发器,避免后台静默消耗配额。
  • 如果业务场景确实需要更高的调用量,可升级至对应版本的Google Workspace账号获取更高配额;临时紧急使用可等待次日配额重置后再操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 07:42:14