javascript - 使用Drive API/DriveApp将PDF转换为Google文档

标签 javascript google-apps-script google-drive-api

此问题已成功解决。我正在编辑我的帖子,以记录我的经历,以供后代引用。

任务

我有117个PDF文件(平均大小约为238 KB)上传到Google云端硬盘。我想将它们全部转换为Google文档,并将它们保存在其他Drive文件夹中。

问题

我试图使用Drive.Files.insert转换文件。但是,在大多数情况下,在此错误导致函数过早失效之前,只能以这种方式转换5个文件

Limit Exceeded: DriveApp. (line #, file "Code")



上面引用的行是当调用insert函数时的位置。首次调用此函数后,后续调用通常会立即失败,而不会创建其他google文档。

方法

我使用3种主要方法来实现自己的目标。如上所述,其中之一是使用Drive.Files.insert。其他两个涉及使用Drive.Files.copy和发送batch of HTTP requests。 Tanaike建议了这最后两种方法,我建议阅读下面的答案以获取更多信息。 insertcopy函数来自Google Drive REST v2 API,而批处理多个HTTP请求来自Drive REST v3。

使用Drive.Files.insert,我遇到了与执行限制有关的问题(在上面的“问题”部分中有解释)。一种解决方案是多次运行这些功能。为此,我需要一种方法来跟踪转换了哪些文件。为此,我有两个选择:使用电子表格和continuation token。因此,我有4种不同的测试方法:本段中提到的两种方法batching HTTP requests和调用Drive.Files.copy

因为是team drives behave differently from regular drives,所以我觉得有必要尝试每种方法两次,一种方法是其中包含PDF的文件夹是常规的非Team Drive文件夹,另一种方法是在Team Drive下的文件夹。总共,这意味着我要测试 8种不同的方法。

这些是我使用的确切功能。其中每个都使用了两次,唯一的变化是源文件夹和目标文件夹的ID(由于上述原因):

方法A:使用Drive.Files.insert和电子表格
function toDocs() {
  var sheet = SpreadsheetApp.openById(/* spreadsheet id*/).getSheets()[0];
  var range = sheet.getRange("A2:E118");
  var table = range.getValues();
  var len = table.length;
  var resources = {
    title: null,
    mimeType: MimeType.GOOGLE_DOCS,
    parents: [{id: /* destination folder id */}]
  };
  var count = 0;
  var files = DriveApp.getFolderById(/* source folder id */).getFiles();
  while (files.hasNext()) {
    var blob = files.next().getBlob();
    var blobName = blob.getName();
    for (var i=0; i<len; i++) {
      if (table[i][0] === blobName.slice(5, 18)) {
        if (table[i][4])
          break;
        resources.title = blobName;
        Drive.Files.insert(resources, blob);  // Limit Exceeded: DriveApp. (line 51, file "Code")
        table[i][4] = "yes";
      }
    }

    if (++count === 10) {
      range.setValues(table);
      Logger.log("time's up");
    }
  }
}

方法B:使用Drive.Files.insertcontinuation token
function toDocs() {
  var folder = DriveApp.getFolderById(/* source folder id */);
  var sprop = PropertiesService.getScriptProperties();
  var contToken = sprop.getProperty("contToken");
  var files = contToken ? DriveApp.continueFileIterator(contToken) : folder.getFiles();
  var options = {
    ocr: true
  };
  var resource = {
    title: null,
    mimeType: null,
    parents: [{id: /* destination folder id */}]
  };

  while (files.hasNext()) {
    var blob = files.next().getBlob();
    resource.title = blob.getName();
    resource.mimeType = blob.getContentType();
    Drive.Files.insert(resource, blob, options);  // Limit Exceeded: DriveApp. (line 113, file "Code")
    sprop.setProperty("contToken", files.getContinuationToken());
  }
}

方法C:使用Drive.Files.copy

此功能归功于Tanaike-有关更多详细信息,请参见下面的答案。
function toDocs() {
  var sourceFolderId = /* source folder id */;
  var destinationFolderId = /* destination folder id */;
  var files = DriveApp.getFolderById(sourceFolderId).getFiles();
  while (files.hasNext()) {
    var res = Drive.Files.copy({parents: [{id: destinationFolderId}]}, files.next().getId(), {convert: true, ocr: true});
    Logger.log(res) 
  }
}

方法D:发送batches of HTTP requests

此功能归功于Tanaike-有关更多详细信息,请参见下面的答案。
function toDocs() {
  var sourceFolderId = /* source folder id */;
  var destinationFolderId = /* destination folder id */;

  var files = DriveApp.getFolderById(sourceFolderId).getFiles();
  var rBody = [];
  while (files.hasNext()) {
    rBody.push({
      method: "POST",
      endpoint: "https://www.googleapis.com/drive/v3/files/" + files.next().getId() + "/copy",
      requestBody: {
        mimeType: "application/vnd.google-apps.document",
        parents: [destinationFolderId]
      }
    });
  }
  var cycle = 20; // Number of API calls at 1 batch request.
  for (var i = 0; i < Math.ceil(rBody.length / cycle); i++) {
    var offset = i * cycle;
    var body = rBody.slice(offset, offset + cycle);
    var boundary = "xxxxxxxxxx";
    var contentId = 0;
    var data = "--" + boundary + "\r\n";
    body.forEach(function(e){
      data += "Content-Type: application/http\r\n";
      data += "Content-ID: " + ++contentId + "\r\n\r\n";
      data += e.method + " " + e.endpoint + "\r\n";
      data += e.requestBody ? "Content-Type: application/json; charset=utf-8\r\n\r\n" : "\r\n";
      data += e.requestBody ? JSON.stringify(e.requestBody) + "\r\n" : "";
      data += "--" + boundary + "\r\n";
    });
    var options = {
      method: "post",
      contentType: "multipart/mixed; boundary=" + boundary,
      payload: Utilities.newBlob(data).getBytes(),
      headers: {'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()},
      muteHttpExceptions: true,
    };
    var res = UrlFetchApp.fetch("https://www.googleapis.com/batch", options).getContentText();
//    Logger.log(res); // If you use this, please remove the comment.
  }
}

什么有效,什么无效
  • 使用Drive.Files.insert的功能均无效。每一个
    使用insert进行转换的函数因此错误而失败

    Limit Exceeded: DriveApp. (line #, file "Code")



    (行号替换为通用符号)。没有更多详细信息或
    可以找到错误的描述。一个显着的变化是
    在其中我使用了电子表格,而PDF在团队合作中
    夹;而其他所有方法都立即失败,而没有转换
    单个文件,此文件在失败前已转换为5。但是,当
    考虑到为什么这种变体比其他变体更好,我认为
    比起使用特定语言的任何原因,这更是more幸
    资源(电子表格,团队合作精神等)
  • 仅使用Drive.Files.copybatch HTTP requests
    当源文件夹是个人(非Team Drive)文件夹时。
  • 尝试从Team Drive读取时使用copy函数
    文件夹因以下错误而失败:

    File not found: 1RAGxe9a_-euRpWm3ePrbaGaX5brpmGXu (line #, file "Code")



    (行号替换为通用符号)。被引用的行

    var res = Drive.Files.copy({parents: [{id: destinationFolderId}]}, files.next().getId(), {convert: true, ocr: true});
    
  • 从Team Drive文件夹中读取时使用batch HTTP requests
    不执行任何操作-不创建任何文档文件,也不会引发任何错误。
    功能会在没有完成任何操作的情况下静默终止。

  • 结论

    如果您希望将大量PDF转换为google docs或文本文件,请使用Drive.Files.copysend batches of HTTP requests并确保PDF存储在个人驱动器而不是Team Drive中。

    特别感谢@tehhowch对我的问题如此热切的兴趣并多次回覆以提供反馈,以及@Tanaike提供的代码以及成功解决了我的问题的解释(附上警告,请阅读上面的详细信息)。

    最佳答案

    您要将文件夹中的PDF文件转换为Google文档。 PDF文件位于团队驱动器的文件夹中。您想将转换后的文件导入到Google云端硬盘的文件夹中。如果我的理解是正确的,那么这种方法呢?

    对于从PDF到Google Document的转换,它不仅可以使用Drive.Files.insert()进行转换,还可以使用Drive.Files.copy()进行转换。使用Drive.Files.copy()的优点是

  • 尽管Drive.Files.insert()的大小限制为5 MB,但Drive.Files.copy()可以使用超过5 MB的大小。
  • 在我的环境中,处理速度比Drive.Files.insert()快。

  • 对于这种方法,我想提出以下2种模式。

    模式1:使用Drive API v2

    在这种情况下,高级Google服务的Drive API v2用于转换文件。
    function myFunction() {
      var sourceFolderId = "/* source folder id */";
      var destinationFolderId = "/* dest folder id */";
      var files = DriveApp.getFolderById(sourceFolderId).getFiles();
      while (files.hasNext()) {
        var res = Drive.Files.copy({parents: [{id: destinationFolderId}]}, files.next().getId(), {convert: true, ocr: true});
    //    Logger.log(res) // If you use this, please remove the comment.
      }
    }
    

    模式2:使用Drive API v3

    在这种情况下,Drive API v3用于转换文件。在这里,我将批处理请求用于这种情况。因为批处理请求可以通过一个API调用使用100个API调用。这样,可以消除API配额问题。
    function myFunction() {
      var sourceFolderId = "/* source folder id */";
      var destinationFolderId = "/* dest folder id */";
    
      var files = DriveApp.getFolderById(sourceFolderId).getFiles();
      var rBody = [];
      while (files.hasNext()) {
        rBody.push({
          method: "POST",
          endpoint: "https://www.googleapis.com/drive/v3/files/" + files.next().getId() + "/copy",
          requestBody: {
            mimeType: "application/vnd.google-apps.document",
            parents: [destinationFolderId]
          }
        });
      }
      var cycle = 100; // Number of API calls at 1 batch request.
      for (var i = 0; i < Math.ceil(rBody.length / cycle); i++) {
        var offset = i * cycle;
        var body = rBody.slice(offset, offset + cycle);
        var boundary = "xxxxxxxxxx";
        var contentId = 0;
        var data = "--" + boundary + "\r\n";
        body.forEach(function(e){
          data += "Content-Type: application/http\r\n";
          data += "Content-ID: " + ++contentId + "\r\n\r\n";
          data += e.method + " " + e.endpoint + "\r\n";
          data += e.requestBody ? "Content-Type: application/json; charset=utf-8\r\n\r\n" : "\r\n";
          data += e.requestBody ? JSON.stringify(e.requestBody) + "\r\n" : "";
          data += "--" + boundary + "\r\n";
        });
        var options = {
          method: "post",
          contentType: "multipart/mixed; boundary=" + boundary,
          payload: Utilities.newBlob(data).getBytes(),
          headers: {'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()},
          muteHttpExceptions: true,
        };
        var res = UrlFetchApp.fetch("https://www.googleapis.com/batch", options).getContentText();
    //    Logger.log(res); // If you use this, please remove the comment.
      }
    }
    

    注意 :
  • 如果在1个批处理请求中API调用的数量很大(当前值为100),请修改var cycle = 100
  • 如果Drive API v3无法用于团队合作,请告诉我。我可以将其转换为Drive API v2。
  • 如果是您遇到问题的原因是团队驱动器,那么在将PDF文件复制到Google云端硬盘后,您可以尝试这样做吗?

  • 引用:
  • Batching Requests

  • 如果这些对您没有用,对不起。

    关于javascript - 使用Drive API/DriveApp将PDF转换为Google文档,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49313477/

    相关文章:

    javascript - 使用 Javascript 通过电子邮件发送 Google 表单数据

    javascript - Google Drive 的文件夹结构是否可能包含循环?

    javascript - 将数据从一个开发工具发送到另一个开发工具

    javascript - 测试使用闭包变量的 Javascript 函数的最佳方法?

    javascript - JQuery Mobile 单击控制组内不起作用

    html - 使用 Google Apps 脚本在电子邮件正文中将 Google Doc 作为 HTML 发送,同时保留格式

    google-apps-script - Google 文档 - 脚本,将单元格从一张纸复制到另一张纸

    javascript - 在 Apps 脚本函数中获取 html 文本框的值

    java - 如何通过 Java 代码获取访问 Google 应用程序的临时持久凭据

    javascript - 无法解析模块的webpack配置错误