How to upload an existing file in Drive to Drive using Apps Script

前端 未结 2 1685
粉色の甜心
粉色の甜心 2021-01-19 02:49

Introduction

Let me first introduce the goal of what I am trying to do.

  • I had a file split into two parts earlier

  • Si

相关标签:
2条回答
  • From your question and replying, I could understand your situation and goal like below.

    • In your sample files, "file A" and "file B" are 524,288 bytes and 163,339 bytes, respectively.
    • Your tested script is the script showing in your question.
    • You want to merge the files using the resumable upload.
    • The mimeType of the merged file is PDF.

    For this, how about this answer?

    Modification points:

    • Unfortunately, your script is incomplete for achieving the resumable upload. The flow of the resumable upload at Google Drive API is as follows. Ref

      1. Request for retrieving the location which is used as the endpoint for uploading data.
        • In your case, the new file is created. So it is required to use the POST method.
      2. Request to the retrieved location by including the data (in your case, it's each file.).
        • In this case, it is required to upload the data using a loop. And the PUT method is used.
        • Here, each file size is most important. If the file size except for the last file is not the multiples of 262,144 bytes, the resumable upload cannot be run by an error. Please be careful this.

    For above flow, when the sample script is prepared, it becomes as follows.

    Usage:

    1. Enable Drive API.

    In this case, Drive API is used. So please enable Drive API at Advanced Google Services. By this, the Drive API is automatically enabled at API console.

    The flow of the sample script is as follows.

    1. Create an object for using at the resumable upload.
    2. Retrieve "location" for starting the resumable upload.
    3. Upload each file and merge them.

    2. Sample script.

    Please copy and paste the following script. And please set the file IDs. In this case, please set them in order for merging. Please be careful this.

    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.
    }
    

    Result:

    When the resumable upload is finished, the following result can be seen at the log. And you can see the merged file at the root folder.

    {
     "kind": "drive#file",
     "id": "###",
     "name": "sample.pdf",
     "mimeType": "application/pdf"
    }
    

    Note:

    • This is a simple sample script. So please modify this for your actual situation.
    • I tested above script for your sample situation that "file A" and "file B" are 524,288 bytes and 163,339 bytes. So when several files with about 50 MB in size are merged using this script, an error occurs.
    • If the memory error occurs when the large files are used, in the current stage, it seems that this is the specification of Google side. So please be careful this.

    Reference:

    • Perform a resumable upload
    0 讨论(0)
  • 2021-01-19 03:13

    Tanaike's answer is more than perfect. It's elegant and has even helped me to learn about array.reduce function. Before I asked this question, I had minimal knowledge about JavaScript and almost zero knowledge in using Google Drive API.

    My intention was to learn the whole process of resumable upload step by step using Google Apps Script as the language. Using Tanaike's code as reference I wrote a script which instead of being productive, manageable, and elegant would provide myself (at least) an idea of how resumable upload works step by step. I have used no loops, no objects, and even no arrays.

    Step 1 ( Declare the necessary variables )

      var fileId1 = "XXXXXXXXXXX"; //id of the first file
      var fileId2 = "YYYYYYYYYYY"; //id of the second file
      var filename = "merged.pdf"; //name of the final merged file
      var mimeType = MimeType.PDF; //Mime type of the merged file
    

    Step 2 ( Initiate the resumable upload )

    //declare the end point
    const url = "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable";
    
    //Send the request
    //Method to be used is Post during initiation
    //No file is to be sent during initiation
    //The file name and the mime type are sent
    const res1 = UrlFetchApp.fetch(url, {
        method: "post",
        contentType: "application/json",
        payload: JSON.stringify({name: filename, mimeType: mimeType}),
        headers: {authorization: "Bearer " + ScriptApp.getOAuthToken()
      }});
    

    Step 3 ( Save the resumable session URI )

    const location = res1.getHeaders().Location;
    

    Step 4 (a) ( Upload file 1 )

    Note : Step 4 (a) and (b) can be performed using a loop. In my case, I used it two times without loop

      var file = DriveApp.getFileById(fileId1); //get the first file
      var data = file.getBlob().getBytes(); //get its contents in bytes array
    
    //Method used is PUT not POST
    //Content-Range will contain the range from starting byte to ending byte, then a slash
    //and then file size
    //bytes array of file's blob is put in data
      var params = {
        method : "put",
        headers : {
          'Content-Range' : `bytes 0-524287/687627`
        },
        payload : data,
        muteHttpExceptions: true
      }; 
    
    //Request using Resumable session URI, and above params as parameter
    
      var result = UrlFetchApp.fetch(location,params);
    

    Step 4 (b) ( Upload the second file )

    //Almost same as Step 4 (a)
    //The thing that changes is Content Range
    file = DriveApp.getFileById(fileId2);
      data = file.getBlob().getBytes();
    
      params = {
        method : "put",
        headers : {
          'Content-Range' : `bytes 524288-687626/687627`
        },
        payload : data,
        muteHttpExceptions : true
      };
    
      result = UrlFetchApp.fetch(location, params);
    

    Now instead of doing step 4 n number of times, it's better to use a loop.

    Also, this code doesn't checks for possible error that might have occurred during the process.

    Hope this code helps someone, even though it was more of a self-teaching experiment. :)

    0 讨论(0)
提交回复
热议问题