【问题标题】:Error when creating multiple PDFs in Google Apps Script在 Google Apps 脚本中创建多个 PDF 时出错
【发布时间】:2019-12-17 23:18:05
【问题描述】:

我创建了一个将电子表格(工作表)作为 PDF 保存到指定文件夹的函数。该功能运行良好,但是当我多次运行它时(我需要执行 20 次),在第 7 次、第 8 次或第 9 次运行后出现错误。错误是 429。它没有给我很多信息,我似乎无法找到错误是什么以及如何纠正。我已经尝试添加一个 Utilities.sleep(xxx) 并且当我进行 5 秒睡眠时它确实有效(但当它少于 5 秒时则不行)!

这是我的代码(使用 Utilities.sleep):

/**
 * Creates a PDF file 
 *
 * 2019-12-17 Simon: Created
 *
 * @param {?} token                ScriptApp.getOAuthToken();
 * @param {?} spreadsheet          Spreadsheet (SpreadsheetApp.getActiveSpreadsheet())
 * @param {string} sheetName       Name of the sheet to print
 * @param {string} pdfName         Name of the pdf file (excluding .pdf)
 * @param {string} folder          Folder to save in
 * @param {string} portrait        true=portrait, false=landscape
 * @param {number} scale           1 = Normal 100% -- 2 = Fit to width -- 3 = Fit to height -- 4 = Fit to Page
 * @param {number} margins         In inches. Dot as decimal separator, e.g. '0.2'
 * @param {string} range           Optional. E.g. 'D4:AX74'
 */ 
function savePdf(spreadsheet, sheetName, pdfName, folder, portrait, scale, margins, range) { 
  var rangeUse = (range ? '&range=' + range : '');
  var sheetId = spreadsheet.getSheetByName(sheetName).getSheetId();
  var url_base = spreadsheetId.getUrl().replace(/edit$/,'');
  var url_ext = 'export?'
  + '&gid=' + sheetId  
  + rangeUse
  + '&format=pdf'                   // export format
  + '&size=a4'                      // A3/A4/A5/B4/B5/letter/tabloid/legal/statement/executive/folio
  + '&portrait=' + portrait         // true = Potrait / false= Landscape
  + '&scale=' + scale               // 1 = Normal 100% -- 2 = Fit to width -- 3 = Fit to height -- 4 = Fit to Page
  + '&top_margin=' + margins        // all four margins must be set!
  + '&bottom_margin=' + margins     // all four margins must be set!
  + '&left_margin=' + margins       // all four margins must be set!
  + '&right_margin=' + margins      // all four margins must be set!
  + '&gridlines=false'              // true/false
  + '&printnotes=false'             // true/false
  + '&pageorder=2'                  // 1 = Down, then over -- 2 = Over, then down
  + '&horizontal_alignment=CENTER'  // LEFT/CENTER/RIGHT
  + '&vertical_alignment=MIDDLE'    // TOP/MIDDLE/BOTTOM
  + '&printtitle=false'             // print title --true/false
  + '&sheetnames=false'             // print sheet names -- true/false
  + '&fzr=true'                     // repeat row headers (frozen rows) on each page -- true/false
  + '&fzc=true'                     // repeat column headers (frozen columns) on each page -- true/false
  + '&attachment=false'             // true/false
  var token = ScriptApp.getOAuthToken();
  var url_options = {headers: {'Authorization': 'Bearer ' + token, 'muteHttpExceptions': true,}};
  Utilities.sleep(5000);
  var response = UrlFetchApp.fetch(url_base + url_ext, url_options);
  var blob = response.getBlob().getAs('application/pdf').setName(pdfName + '.pdf');
  folder.createFile(blob);
}

【问题讨论】:

  • 429 请求太多。 5 秒的延迟解释了为什么这会消失。
  • 嗨,SiKing,谢谢你——你知道有什么方法可以找出哪个请求是“太多”吗?循环函数 7、8 og 9 次后出现错误。
  • 我怀疑这将是一个特定的请求。他们可能会跟踪您的所有请求,并将您限制为每 Y 次 X 次请求。

标签: pdf google-apps-script google-sheets urlfetch http-status-code-429


【解决方案1】:

更改了脚本以在开始时制作电子表格的副本并处理该副本 - 最后将其丢弃。

这是最终的工作脚本:

    /**
     * Creates a PDF file 
     *
     * 2019-12-17 Simon: Created
     *
     * @param {?} spreadsheet          Spreadsheet (SpreadsheetApp.getActiveSpreadsheet())
     * @param {string} sheetName       Name of the sheet to print
     * @param {string} pdfName         Name of the pdf file (excluding .pdf)
     * @param {string} folder          Folder to save in
     * @param {string} portrait        true=portrait, false=landscape
     * @param {number} scale           1 = Normal 100% -- 2 = Fit to width -- 3 = Fit to height -- 4 = Fit to Page
     * @param {number} margins         In inches. Dot as decimal separator, e.g. '0.2'
     * @param {string} range           Optional. E.g. 'D4:AX74'
     */ 
    function savePdf(spreadsheet, sheetName, pdfName, folder, portrait, scale, margins, range) { 
      var rangeUse = (range ? '&range=' + range : '');
      var ssNew = spreadsheet.copy('temp');
      var sheetId = spreadsheet.getSheetByName(sheetName).getSheetId();
      var url_base = spreadsheet.getUrl().replace(/edit$/,'');
      var url_ext = 'export?'
      + '&gid=' + sheetId  
      + rangeUse
      + '&format=pdf'                   // export format
      + '&size=a4'                      // A3/A4/A5/B4/B5/letter/tabloid/legal/statement/executive/folio
      + '&portrait=' + portrait         // true = Potrait / false= Landscape
      + '&scale=' + scale               // 1 = Normal 100% -- 2 = Fit to width -- 3 = Fit to height -- 4 = Fit to Page
      + '&top_margin=' + margins        // all four margins must be set!
      + '&bottom_margin=' + margins     // all four margins must be set!
      + '&left_margin=' + margins       // all four margins must be set!
      + '&right_margin=' + margins      // all four margins must be set!
      + '&gridlines=false'              // true/false
      + '&printnotes=false'             // true/false
      + '&pageorder=2'                  // 1 = Down, then over -- 2 = Over, then down
      + '&horizontal_alignment=CENTER'  // LEFT/CENTER/RIGHT
      + '&vertical_alignment=MIDDLE'    // TOP/MIDDLE/BOTTOM
      + '&printtitle=false'             // print title --true/false
      + '&sheetnames=false'             // print sheet names -- true/false
      + '&fzr=true'                     // repeat row headers (frozen rows) on each page -- true/false
      + '&fzc=true'                     // repeat column headers (frozen columns) on each page -- true/false
      + '&attachment=false'             // true/false
      var token = ScriptApp.getOAuthToken();
      var url_options = {headers: {'Authorization': 'Bearer ' + token, 'muteHttpExceptions': true,}};
      var response = UrlFetchApp.fetch(url_base + url_ext, url_options);
      var blob = response.getBlob().getAs('application/pdf').setName(pdfName + '.pdf');
      folder.createFile(blob);
      DriveApp.getFileById(ssNew.getId()).setTrashed(true);
    }

【讨论】:

  • 我喜欢简单的解决方案!这将创建相当多的“已删除”版本,这些版本可能会使您在云端硬盘中的已删除文件变得混乱(即使每天创建 20 个 pdf 文件,也会在垃圾箱中生成 1200 个重复文件)。如果您从不查看垃圾箱或将垃圾箱用于任何用途,但如果完成工作,我看不到任何其他后果。
  • @SimonGade (已经路过) 请不要对没有外部依赖或平台无法运行的脚本使用代码sn-ps。请改用正确的代码格式 [Ctrl+K]:单行反引号(“`”)、属性名称和方法、代码块的代码围栏(“````”)。另外请避免在问题中闲聊,SO不是论坛,而是问答网站。
【解决方案2】:

我曾经遇到过这个问题,并且能够通过从函数的递归部分中删除对 ScriptApp.getOAuthToken() 的调用来修复它。我认为在您的情况下最简单的方法是使用 CacheService。

替换var token = ScriptApp.getOAuthToken();这一行

与:

var token;
  if(CacheService.getScriptCache().get('token')!=null) {
    token = CacheService.getScriptCache().get('token');
  } else {
    token = ScriptApp.getOAuthToken();
    CacheService.getScriptCache().put('token',token,120);   
  }

这会将令牌值存储在 CacheService 中,而不是使用脚本循环递归调用它。希望这能像解决我的问题一样解决您的问题。

编辑:

在上述没有解决您的问题之后,我回顾了我所做的并错误地认为它正在获取导致我的问题的令牌,但这是从 Google Sheets API 导出的速率限制。以下是我当时注意到的实际解决了我的问题:

速率限制(有关我的想法,请参见我的最后一段)是按 SHEET 而不是每个用户 - 我的递归脚本当时正在访问两个不同的工作表,因此函数中的自然延迟创建了足够的时间延迟我的脚本运行它的过程没有问题。

现在来修复你的戏剧:

复制您的问题后,我修改了父函数以创建主电子表格的副本:

var mainsheetcopy = mainsheet.copy('Copy of main sheet')

然后在两个电子表格之间切换以将调用发送到函数以提取 PDF。我能够迭代提取 20 个 pdf,睡眠延迟仅为 750 毫秒,并且 18 次迭代完全没有延迟。

for(var i=0; i<20; i++) {
    if(isEven(i)) {
      sheetid = mainsheet.getId();
    } else { 
      sheetid = mainsheetcopy.getId()} 

下面是isEven函数供参考:

function isEven(n) {
   return n % 2 == 0;
}

然后,在脚本的末尾,我删除了副本:

DriveApp.getFileById(mainsheetcopy.getId()).setTrashed(true);

如果时间是一个因素,您可以创建主电子表格的第三个副本,并且仅通过该功能自然发生的延迟,它会使您超出将工作表导出为 PDF 的速率限制。

实际的速率限制有点难以捉摸,但每 7.5-8 秒一张纸似乎绕过了这一点。我能够在 100% 的时间和偶尔 6 个重复每张表导出的最多 5 个 PDF 文件,没有速率限制。

【讨论】:

  • 感谢您的建议,罗布。它没有用:-(我也尝试过(之前)将 getOAuthToken 放在调用函数中,所以它只运行了一次并且没有帮助。所以它一定是别的东西——我只是不知道是什么......
  • 我已经能够复制您的问题,并创建了一个解决方法,这将有助于加快该功能,但确实存在通过 Google Sheets API 导出 PDF 的速率限制他们的 API 中似乎没有记录。
  • 非常感谢!我让它工作了。我稍微修改了一下,“只是”在剧本的开头复制了一份工作表并处理了那个副本——当然最后把它扔掉了。我会接受你的回答并编写我的脚本。
猜你喜欢
  • 2014-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多