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

在Google Apps脚本中创建多个PDF时出错,pdf,google-apps-script,google-sheets,urlfetch,http-status-code-429,Pdf,Google Apps Script,Google Sheets,Urlfetch,Http Status Code 429,我创建了一个函数,可以将电子表格(工作表)以PDF格式保存到指定的文件夹中。该函数工作得很好,但当我多次运行它(我需要做20次)时,在第7次、第8次或第9次运行后,我会得到一个错误。错误是429。它并没有给我很多信息,我似乎也找不到错误是什么以及如何纠正。我尝试添加了一个实用程序。sleep(xxx),当我进行5秒睡眠时,它确实起作用(但当睡眠时间少于5秒时则不起作用) 以下是我的代码(带有实用程序.sleep): 我曾经遇到过这个问题,并且能够通过从函数的递归部分删除对ScriptApp.ge

我创建了一个函数,可以将电子表格(工作表)以PDF格式保存到指定的文件夹中。该函数工作得很好,但当我多次运行它(我需要做20次)时,在第7次、第8次或第9次运行后,我会得到一个错误。错误是429。它并没有给我很多信息,我似乎也找不到错误是什么以及如何纠正。我尝试添加了一个实用程序。sleep(xxx),当我进行5秒睡眠时,它确实起作用(但当睡眠时间少于5秒时则不起作用)

以下是我的代码(带有实用程序.sleep):


我曾经遇到过这个问题,并且能够通过从函数的递归部分删除对
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导出的速率限制。以下是我当时注意到的解决问题的方法:

速率限制(关于这一点,请参见我的最后一段)是每个工作表,而不是每个用户——我的递归脚本当时正在访问两个不同的工作表,因此函数中的自然延迟产生了足够的时间延迟,使我的脚本能够顺利运行

现在为您的戏剧进行修复:

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

var mainsheetcopy = mainsheet.copy('Copy of main sheet')
然后在两个电子表格之间切换,将调用发送到函数以提取PDF。我能够反复提取20个PDF,睡眠延迟仅为750ms,18次迭代完全没有延迟

for(var i=0; i<20; i++) {
    if(isEven(i)) {
      sheetid = mainsheet.getId();
    } else { 
      sheetid = mainsheetcopy.getId()} 
然后,在脚本末尾,我删除了副本:

DriveApp.getFileById(mainsheetcopy.getId()).setTrashed(true);
如果时间是一个因素,那么您可以创建主电子表格的第三个副本,并且仅通过函数自然发生的延迟,它将使您超出将工作表导出为PDF的速率限制


实际的速率限制有点难以捉摸,但每7.5-8秒一张纸似乎可以绕过这一限制。我能够在100%的时间内迭代每个工作表最多5个导出的PDF文件,没有速率限制,偶尔可以迭代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);
    }

429请求太多了。5秒的延迟解释了为什么这会消失。嗨,SiKing,谢谢你——你知道有什么方法可以找出哪个请求“太多”吗?我在循环函数7、8、9次后得到错误。我怀疑这是否是一个特定的请求。他们可能会跟踪您的所有请求,并将您的每次请求限制为X个。这是否回答了您的问题?谢谢你的建议,罗伯。它不起作用:-(我也试过(早些时候)将getOAuthToken放入调用函数中,使其只运行一次,但这没有帮助。因此,这一定是其他原因–我不知道是什么…我已经能够复制您的问题,并创建了一个解决方案,这将有助于加快函数的运行速度,但通过Google Sheets API导出PDF确实有一个速率限制,该限制不起作用他们的API中似乎没有文档。非常感谢!我让它工作了。我对它做了一些修改,“只是”在脚本的开头复制了一份工作表,然后继续制作该副本,最后当然会将其销毁。我会接受你的答案并编写我的脚本。我喜欢简单的解决方案!这将创建相当多的“垃圾”版本,可能会将你在驱动器中的垃圾文件弄得乱七八糟(即使每天一次创建20个PDF,也会在垃圾箱中生成1200个副本)。如果你不在垃圾箱中查找或使用任何东西,我看不到如果这样做会产生任何其他后果。@SimonGade(一直路过)请不要对没有外部依赖项或平台就无法运行的脚本使用代码段。请改用正确的代码格式[Ctrl+K]:单行线使用单回号(“`”),属性名称和方法使用单回号(“`”),代码块使用代码围栏(“``”)。此外,请避免闲聊问题,因为这不是一个论坛,而是一个问答网站。
DriveApp.getFileById(mainsheetcopy.getId()).setTrashed(true);
    /**
     * 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);
    }