在 Google Apps 脚本中创建多个 PDF 时出错

Error when creating multiple PDFs in Google Apps Script

我创建了一个函数,可以将 spreadsheet (sheet) 作为 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);
}

我 运行 进入过一次,并且能够通过从函数的递归部分中删除对 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 张 API。这是我当时注意到的实际上解决了我的问题:

速率限制(请参阅我的最后一段以了解我对此的想法)是针对每个 SHEET 而不是针对每个用户的——当时我的递归脚本正在访问两个不同的 sheet,所以函数中的自然延迟为我的脚本创建了足够的时间延迟 运行 它的过程没有问题。

现在修复你的戏剧:

复制你的问题后,我修改了我的父函数来创建主传播的副本sheet:

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

然后在两个 spreadsheet 之间切换以发送对函数的调用以提取 PDF。我能够以仅 750 毫秒的睡眠延迟迭代提取 20 个 pdf,并且在没有任何内置延迟的情况下进行 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);

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

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

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

这是最终的工作脚本:

    /**
     * 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);
    }