在 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);
}
我创建了一个函数,可以将 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);
}