【问题标题】:Undocumented Sheet API limitation problems未记录的工作表 API 限制问题
【发布时间】:2018-10-19 22:00:27
【问题描述】:

我已经看过人们遇到类似问题的帖子,但找不到明确的答案。

我尝试使用以下代码行检索 264735 个插槽的二维数组:

  var optionalArguments = {majorDimension: "ROWS",
                     valueRenderOption: "FORMULA",
  };
  var sourceValuesObject = Sheets.Spreadsheets.Values.get(spreadsheetId, rangeA1Notation, optionalArguments)

但这是我得到的:

响应代码:413。消息:响应太大。

这看起来很奇怪,因为我看不到这种无处可写的限制以及如果 300000 个或更少的单元格会导致 API 错误,用户如何获取大量数据。

我尝试拆分请求并且它可以工作,但这使我的代码更加复杂和缓慢,而且在尝试将值更新回工作表范围时我得到一个空响应。

我指向正确的方向吗,这是否正常,有解决方法吗?

编辑: Here is a sample spreasheet

首先我尝试使用Sheets.Spreadsheets.Values.get 获取拆分范围 在for 循环中,它起作用了。

batchGet 做同样的事情给了我同样的错误,所以我猜我的单元格内的数据太大了。

【问题讨论】:

  • 您能否提供一个示例电子表格来复制您的情况?我认为它会帮助用户思考你的问题。
  • 您确定它是 Sheets API 而不是 Apps 脚本方面的限制吗?也许尝试使用另一种语言的 api。 UrlFetch 调用的最大响应大小为 50MB/调用。同样的限制可能适用于 api 调用。
  • @Tanaike appart from the rangeA1Notationvariable 这是唯一使它出错的代码。另外,我不能真正分享我的代码,我需要征求许可,但谢谢。
  • @I'-'我认为您的第一条评论是正确的。实际上,通过使用 OP 的共享电子表格进行的实验,发现您的评论是正确的。
  • @I'-'我非常感谢你。虽然像批量请求这样的方法可以通过一个 API 调用来调用多个请求,但 UrlFetchApp 的最大大小成为瓶颈。我认为在检索大尺寸值的情况下,UrlFetchApp.fetchAll() 比批量请求更合适。

标签: javascript google-apps-script google-sheets-api http-status-code-413


【解决方案1】:

这个解决方法怎么样?

实验:

此实验使用您共享的示例电子表格。

UrlFetchApp 直接调用 Sheets API 的端点时,如果响应大小大于 50 MB(52,428,800 Byte),则返回小于 50 MB 的响应。 50 MB 的大小是由于 UrlFetchApp 的限制。另一方面,在高级谷歌服务中,它无法确认这种情况,因为超出限制时会发生错误。因此,通过使用 UrlFetchApp,可以确认您的情况出现错误的原因。所以起初,我使用以下脚本确认了这一点。

var spreadsheetId = "#####";
var range = "'Copie de Feuille 1'!A1:JE1000";
var url = "https://sheets.googleapis.com/v4/spreadsheets/" + spreadsheetId + "/values/" + range + "?majorDimension=ROWS&valueRenderOption=FORMULA";
var res = UrlFetchApp.fetch(url, {headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()}});
Logger.log(res.getContentText().length.toString())
var values = JSON.parse(res.getContentText());

运行上述脚本时,返回52428450。最后一行出现“未终止的字符串文字”的错误。这意味着对象不完整。从这个结果中可以发现,在'Copie de Feuille 1'!A1:JE1000的范围内,一次调用values.get是无法获取到值的。这与I'-'I`s comment 的情况相同。

在您的示例电子表格中,发现错误发生的范围边界是'Copie de Feuille 1'!A1:JE722。当尝试从'Copie de Feuille 1'!A1:JE723 范围内检索值时,会发生错误。从'Copie de Feuille 1'!A1:JE722 检索到的值的大小为 52,390,229 字节。这小于 50 MB(52,428,800 字节)。 'Copie de Feuille 1'!A1:JE723 的大小为 52,428,450 字节,与 'Copie de Feuille 1'!A1:JE1000 的值相同。由此发现,是超出了UrlFetchApp的限制。

解决方法:

为了避免此错误并检索所有值,作为一种解决方法,我认为我想建议它拆分范围以从电子表格中检索值。但在你的问题中,你想要速度。所以我想提出以下示例脚本。

  1. 创建请求。
  2. 使用UrlFetchApp.fetchAll() 获取创建的请求。
    • 通过UrlFetchApp.fetchAll(),每个请求都可以异步处理。
    • 在高级 Google 服务的 Sheets API 中,无法使用。而且,在 values.batchGet 处,因为所有检索到的值都超过了限制,所以会发生错误。
    • 这样一来,使用UrlFetchApp.fetchAll() 的流程成本将低于Advanced Google Service 的Sheets API。

示例脚本:

var ranges = ["'Copie de Feuille 1'!A1:JE500", "'Copie de Feuille 1'!A501:JE1000"]; // This was used from the shared spreadsheet. So please modify this for your environment.
var token = ScriptApp.getOAuthToken();
var requests = ranges.map(function(e) {
  return {
    method: "get",
    url: "https://sheets.googleapis.com/v4/spreadsheets/" + spreadsheetId + "/values/" + e + "?majorDimension=ROWS&valueRenderOption=FORMULA",
    headers: {Authorization: "Bearer " + token},
    muteHttpExceptions: true,
  }
});
var res = UrlFetchApp.fetchAll(requests);
var values = res.reduce(function(ar, e) {
  Array.prototype.push.apply(ar, JSON.parse(e.getContentText()).values);
  return ar;
}, []);
Logger.log(values.length) // 1000
Logger.log(values[0].length) // 265

注意:

  • 如果您要使用脚本,请确认在 API 控制台中启用了 Sheets API。
  • 数组合并时,如果出现数组限制错误,请使用每个数组,不要合并数组。
  • 在使用Advanced Google Service的Sheets API时,'Copie de Feuille 1'!A1:JE722范围内也不会出现错误,'Copie de Feuille 1'!A1:JE723范围内也会出现错误。这个结果和UrlFetchApp的结果是一样的。

参考资料:

【讨论】:

  • 这个答案帮助我非常有效地处理了 Google Apps Script http 请求太大 (>50MB) 的问题。用干净的代码给出明确的答案。干杯!对于我的情况,我必须使用 Array.prototype.push.apply(ar, JSON.parse(e.getContentText("UTF-8"))); (UTF-8 并且没有 .values,因为我从 REST API 而不是电子表格中获取)
  • @Frank 感谢您的评论。
【解决方案2】:

这是我通过尝试比较不同的方式来获取值的结果:

function compare(){
  getValuesSpreasheetApp();
  getValuesUrlFetch();
  getValues();
}
//using spreadsheetApp.getFormulas()
function getValuesSpreasheetApp(){
  var t0 = new Date().getTime();
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var dataRange = ss.getActiveSheet().getDataRange();
  var formulas = dataRange.getFormulas();
  var values = dataRange.getValues();
  var t1 = new Date().getTime();
  Logger.log("spreadsheetApp: " + (t1 -t0));
}
//using UrlFetchApp.fetchAll
function getValuesUrlFetch()
{
  var t0 = new Date().getTime();
  var ranges = ["'Copie de Feuille 1'!A1:JE500", "'Copie de Feuille 1'!A501:JE1000"];
  var token = ScriptApp.getOAuthToken();
  var spreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
  var requests = ranges.map(function(e) {
      return {
      method: "get",
      url: "https://sheets.googleapis.com/v4/spreadsheets/" + spreadsheetId + "/values/" + e + "?majorDimension=ROWS&valueRenderOption=FORMULA",
      headers: {Authorization: "Bearer " + token},
      muteHttpExceptions: true,
    }
  });
  var res = UrlFetchApp.fetchAll(requests);
  var values = res.reduce(function(ar, e) {
    Array.prototype.push.apply(ar, JSON.parse(e.getContentText()).values);
    return ar;
  }, []);
  var t1 = new Date().getTime();
  Logger.log("UrlFetch: " + (t1 -t0));
}
//using Sheets.Spreadsheets.Values.get()
function getValues(){
  var t0 = new Date().getTime();
  var spreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
  var ranges = ["'Copie de Feuille 1'!A1:JE500", "'Copie de Feuille 1'!A501:JE1000"];
  var optionalArguments = {majorDimension: "ROWS",
                 valueRenderOption: "FORMULA",
 };
  res = [];
  for(var i = 0; i < ranges.length; i++)
    res.push(Sheets.Spreadsheets.Values.get(spreadsheetId, ranges[i]));
  var values = res.reduce(function(ar, e) {
    Array.prototype.push.apply(ar, e.values);
    return ar;
  }, []);
  var t1 = new Date().getTime();
  Logger.log("values.get: " + (t1 -t0));
}

结果如下:

[18-10-23 01:35:30:034 CEST] 电子表格应用程序:13828

[18-10-23 01:35:33:614 CEST] UrlFecth: 3580

[18-10-23 01:35:38:185 CEST] values.get: 4570

感谢@Tanaike 在错误发生时给了我如此准确的答案,并给了我一个替代解决方案。 这是基准测试,fetchAll 是最快的解决方案。

【讨论】:

  • 我能问一下你的剧本吗?
  • 确定你想知道什么?
  • 谢谢。 getValues() 似乎没有使用var optionalArguments = {majorDimension: "ROWS", valueRenderOption: "FORMULA"};。如果我的理解是正确的,在这种情况下,检索到的值与其他值不同。
  • 哦,你说得对,我会更正一下,看看有什么不同。非常感谢你
猜你喜欢
  • 1970-01-01
  • 2011-01-13
  • 1970-01-01
  • 1970-01-01
  • 2020-11-18
  • 1970-01-01
  • 1970-01-01
  • 2019-04-28
  • 2021-06-12
相关资源
最近更新 更多