google-apps-script - 如何使用 Apps 脚本将 Drive 中的现有文件上传到 Drive

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

简介
让我先介绍一下我尝试做的目标。

  • 我之前有一个文件分成两部分
  • 这两个文件的总大小可能超过 50 MB(作为长期目标)。自 UrlFetchApp.fetch()对请求的大小有限制,我想单独上传它们,每个文件将小于 50 MB,因此合并它们。现在(尝试 Drive API),我使用的是小文件。
  • 第一个文件是 640000 bytes (256 的倍数)524288 bytes .我意识到我之前犯了一个错误,即我使用文件大小作为 256 的倍数,但它应该是 256*1024 的倍数
  • 第二个文件是 47626 bytes 163339 bytes .
  • 我使用 curl 拆分文件并将它们上传到我的驱动器(正常的网络上传)。
  • 我的意图是上传partial files一一使用Resumable Upload到 Google Drive 使用 Google Drive API来自 Google Apps Script以便它们可以合并到一个文件中。

  • 到目前为止我尝试过什么?
  • 昨天,我问了一个 question这里。我试图执行 resumable upload使用 Drive.Files.insert并且用户指出无法使用 Drive.Files.insert下面引用。

  • Unfortunately, in the current stage, the resumable upload cannot be achieved using Drive.Files.insert. It seems that this is the current specification at Google side


  • 我现在正在尝试使用 Google Drive API .下面附上代码。
  • function myFunction() {
        var token = ScriptApp.getOAuthToken();
    
        var f1_id = '1HkBDHV1oXXXXXXXXXXXXXXXXXXXXXXXX';
        var f2_id = '1twuaKTCFTXXXXXXXXXXXXXXXXXXXX';
        
        var putUrl = 'https://www.googleapis.com/drive/v3/files?uploadType=resumable';
      
        var fileData = {
            name : 'Merged-file-from-GAS',
            file : DriveApp.getFileById(f1_id).getBlob()
        }
        
        var options = {
          method : 'put',
          contentType:"application/json",
          headers : {
            Authorization: 'Bearer ' + token,
            'X-Upload-Content-Type' : 'application/octet-stream',
            'Content-Type' : 'application/json; charset=UTF-8'
          },
          muteHttpExceptions: true,
          payload : fileData
        };
      
        var response = UrlFetchApp.fetch(putUrl, options);
        Logger.log(response.getResponseCode());
        Logger.log(response.getAllHeaders()); 
    }
    
    
  • 我也尝试将方法更改为 patch
  • 我加了 Content-Length : 640000headers在这种情况下,我收到如下提供的错误。

  • Exception: Attribute provided with invalid value: Header:Content-Length


  • 我尝试使用 Drive.Files.insert(resource) 创建文件使用空白 resource .然后我尝试使用 UrlFetchApp(patchUrl,options) 更新它在拥有变量的同时var patchUrl = 'https://www.googleapis.com/upload/drive/v3/files/' + fileId + '?uploadType=resumable';

  • 结果
  • 它不会创建任何文件。
  • 下面提供了附加代码(初始代码)结果的记录器日志:

  • [20-05-12 21:05:37:726 IST] 404.0

    [20-05-12 21:05:37:736 IST] {X-Frame-Options=SAMEORIGIN, Content-Security-Policy=frame-ancestors 'self', Transfer-Encoding=chunked, alt-svc=h3-27=":443"; ma=2592000,h3-25=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q049=":443"; ma=2592000,h3-Q048=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43", X-Content-Type-Options=nosniff, Date=Tue, 12 May 2020 15:35:37 GMT, Expires=Mon, 01 Jan 1990 00:00:00 GMT, X-XSS-Protection=1; mode=block, Content-Encoding=gzip, Pragma=no-cache, Cache-Control=no-cache, no-store, max-age=0, must-revalidate, Vary=[Origin, X-Origin], Server=GSE, Content-Type=text/html; charset=UTF-8}


    问题
  • initiating a upload的正确方法是什么?使用 Apps Script 中的 Drive API 将 Drive to Drive 中的文件保存为 upload typeresumable ?
  • 后续请求应该是什么样的?以便随后可以将 50 MB 以上的文件上传到合并文件中?

  • 编辑 1
    使用更正的文件块大小再次尝试。同样的问题仍然存在。
    编辑 2
    为了理解答案中的代码,我使用了// 2中的代码单凭田池的代码就明白了Location被检索。
    function understanding() {
      var token = ScriptApp.getOAuthToken();
      const filename = 'understanding.pdf';
      const mimeType = MimeType.PDF;
    
      const url = 'https://www.googleapis.com/drive/v3/files?uploadType=resumable';
      
      const res1 = UrlFetchApp.fetch(url, {
        method: "post",
        contentType: "application/json",
        payload: JSON.stringify({name: filename, mimeType: mimeType}),
        headers: {authorization: "Bearer " + ScriptApp.getOAuthToken()
      }});
      const location = res1.getHeaders().Location;
      Logger.log(location);
    }
    
    这将创建一个文件 understanding.pdf尺寸0 bytes .然而,Logger.log(location)日志null .
    为什么会这样?
    错误在终点。将其设置为https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable作品。它检索位置。

    最佳答案

    从你的问题和回复中,我可以理解你的情况和目标,如下所示。

  • 在您的示例文件中,“文件 A”和“文件 B”分别为 524,288 字节和 163,339 字节。
  • 您测试的脚本是您的问题中显示的脚本。
  • 您想使用可恢复上传合并文件。
  • 合并文件的 mimeType 为 PDF。

  • 为此,这个答案怎么样?

    retrofit 要点:
  • 不幸的是,您的脚本不完整,无法实现可恢复上传。 Google Drive API 的可恢复上传流程如下。 Ref
  • 请求检索用作上传数据端点的位置。
  • 在您的情况下,将创建新文件。所以需要使用POST方法。
  • 通过包含数据来请求检索到的位置(在您的情况下,它是每个文件。)。
  • 在这种情况下,需要使用循环上传数据。并且使用了 PUT 方法。
  • 在这里,每个文件大小都是最重要的。如果除最后一个文件外的文件大小不是262,144字节的倍数,则无法运行可恢复上传,错误。请注意这一点。

  • 对于上面的流程,当准备好示例脚本时,它变成如下。

    用法:

    1. 启用驱动 API。

    在这种情况下,使用 Drive API。因此,请在高级 Google 服务中启用 Drive API。这样,Drive API 会在 API 控制台自动启用。

    示例脚本的流程如下。
  • 创建一个在可恢复上传时使用的对象。
  • 检索“位置”以开始可恢复上传。
  • 上传每个文件并合并它们。

  • 2. 示例脚本。

    请复制并粘贴以下脚本。并请设置文件 ID。在这种情况下,请设置它们以便合并。请注意这一点。
    function myFunction() {
      const fileIds = ["###", "###"];  // Please set the file IDs of the file "A" and "B" in order.
      const filename = "sample.pdf";
      const mimeType = MimeType.PDF;
    
      // 1. Create an object for using at the resumable upload.
      const unitSize = 262144;
      const fileObj = fileIds.reduce((o, id, i, a) => {
        const file = DriveApp.getFileById(id);
        const size = file.getSize();
        if (i != a.length - 1 && (size % unitSize != 0 || size > 52428800)) {
          throw new Error("Size of each file is required to be the multiples of 262,144 bytes and less than 52,428,800 bytes.");
        }
        o.files.push({data: file.getBlob().getBytes(), range: `bytes ${o.size}-${o.size + size - 1}\/`, size: size.toString()});
        o.size += size;
        return o;
      }, {size: 0, files: []});
    
      // 2. Retrieve "location" for starting the resumable upload.
      const url = "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable";
      const res1 = UrlFetchApp.fetch(url, {
        method: "post",
        contentType: "application/json",
        payload: JSON.stringify({name: filename, mimeType: mimeType}),
        headers: {authorization: "Bearer " + ScriptApp.getOAuthToken()
      }});
      const location = res1.getHeaders().Location;
    
      // 3. Upload each file and merge them.
      fileObj.files.forEach((e, i) => {
        const params = {
          method: "put",
          headers: {"Content-Range": e.range + fileObj.size},
          payload: e.data,
          muteHttpExceptions: true,
        };
        const res = UrlFetchApp.fetch(location, params);
        const status = res.getResponseCode();
        if (status != 308 && status != 200) {
          throw new Error(res.getContentText());
        }
        if (status == 200) {
          console.log(res.getContentText())
        }
      });
    
      // DriveApp.createFile()  // This comment line is used for automatically detecting the scope of "https://www.googleapis.com/auth/drive" by the script editor. So please don't remove this line.
    }
    

    结果:

    可续传上传完成后,在日志中可以看到如下结果。您可以在根文件夹中看到合并后的文件。
    {
     "kind": "drive#file",
     "id": "###",
     "name": "sample.pdf",
     "mimeType": "application/pdf"
    }
    

    笔记:
  • 这是一个简单的示例脚本。所以请根据您的实际情况修改它。
  • 我针对您的示例情况测试了上述脚本,即“文件 A”和“文件 B”分别为 524,288 字节和 163,339 字节。因此,当使用此脚本合并大小约为 50 MB 的多个文件时,会发生错误。
  • 如果在使用大文件时出现内存错误,在现阶段,这似乎是谷歌方面的规范。所以请注意这一点。

  • 引用:
  • Perform a resumable upload
  • 关于google-apps-script - 如何使用 Apps 脚本将 Drive 中的现有文件上传到 Drive,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61756742/

    相关文章:

    google-apps-script - 如何使用 Google Apps 脚本在电子表格的单元格中剪辑文本?

    android - 从 Google Drive 下载文件的缩略图

    google-apps-script - 使用 Google App Script 从 Google Drive 解压缩文件

    python - Google Drive API 权限,如何禁用互联网上任何人都可以找到的功能?

    google-apps - 使用 PHP 代码示例时,“webViewLink”为空/空

    ios - 无法在 iOS 中使用 Google Drive api v3 列出特定文件夹中的文件

    google-apps-script - 从 Google Apps 脚本上的 URL 获取文件 ID 的最简单方法

    google-apps-script - 我的 Google Apps 脚本如何由与工作表共享的其他人运行?

    google-apps-script - 单击按钮时 Google Apps 脚本将电子表格中单元格的值加 1

    javascript - 在 iFrame 中嵌入 Google Apps 脚本