0

我创建了一个将电子表格(工作表)作为 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);
}
4

2 回答 2

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);
    }
于 2019-12-19T14:09:38.630 回答
0

我曾经遇到过这个问题,并且能够通过从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 文件,并且没有速率限制。

于 2019-12-18T06:09:41.673 回答