Upload a file in chunks via resumable upload with the Google Drive API

Upload a file in chunks via resumable upload with the Google Drive API.

Steps involved

 

Google lays out the following steps in their documentation for uploading a file via resumable upload:

A resumable upload consists of three high-level steps:

  1. Send the initial request and retrieve the resumable session URI.
  2. Upload the data and monitor upload state.
  3. (optional) If upload is disturbed, resume the upload.

We’re going to cover the following steps:

  1. Send the initial request to initiate the resumable upload
  2. Send requests to upload content in multiple chunks
  3. Send final request with last chunk of data

1. Send the initial request and retrieve the resumable session URI.

NOTE: I was forced to use XMLHttpRequest for the following reason:

There is a restriction to access response headers when you are using Fetch API over CORS. Due to >this restriction, you can access only following standard headers:

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

This doesn’t work for us, because we need to access the location header on the response. From Google’s docs:

If the initiation request succeeds, the response includes a 200 OK HTTP status code. In addition, it includes a Location header that specifies the resumable session URI.

See this Stack Overflow post for more details

The following code makes the initial request to google drive to initiate the resumable upload. It will contain the URI for the subsequent uploads in the location header of the response:

var xhr = new XMLHttpRequest();
xhr.open(
  "POST",
  `https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable`
);
xhr.onload = function () {
  console.log(this.responseText);
};
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.setRequestHeader("Authorization", `Bearer ${token}`);

xhr.send(
  JSON.stringify({
    name: Title of file to be uploaded,
    mimeType: "video/webm", // Mime type of the file you're uploading
    parents: [driveFolderID], // Optionally, you can specify a parent folder for the file to be uploaded in.  Feel free to remove this
  })
);

xhr.onreadystatechange = function () {
  if (this.readyState == this.HEADERS_RECEIVED) {
    // Get the raw header string
    var headers = xhr.getAllResponseHeaders();

    // Convert the header string into an array
    // of individual headers
    var arr = headers.trim().split(/[\r\n]+/);

    // Create a map of header names to values
    var headerMap = {};
    arr.forEach(function (line) {
      var parts = line.split(": ");
      var header = parts.shift();
      var value = parts.join(": ");
      headerMap[header] = value;
    });
    const resumableURI = headerMap.location
  }
};

2. Send requests to upload content in multiple chunks

NOTE: To send content in multiple chunks, the content has to be sent in multiples of 256KB. The request won’t work if this isn’t true

You’ll need to maintain how much you’ve already uploaded variables. I run the following code after every chunk upload: positionInUpload = positionInUpload + chunkToSend.size;

You’ll also notice that we are using * for the Content-Range header. This was tricky to figure out. In our case, we do this because are streaming a video and don’t yet know the total size of the video. If you know the size of the file you are uploading in advance, feel free to use that size instead of the *.

Lastly, the resumableURI below is the resumableURI we got from the location header of step 1’s response.

let upload_options = {
  method: "PUT",
  headers: {
    Authorization: `Bearer ${token}`,
    "Content-Length": chunkToSend.size,
    "Content-Range": `bytes ${positionInUpload}-${
      positionInUpload + chunkToSend.size - 1
    }/*`,
  },
  body: chunkToSend,
};
const uResponse = await fetch(resumableURI, upload_options);
const uploadResponse = await uResponse.text();

For other Google Drive API Examples: https://www.mikesallese.me/tags/google-drive-api-examples

3. Send the final request with the last chunk of data

This will look similar to step 2, except you will be including the total size in the Content-Range header. I used real numbers in this example for illustration.


  let upload_options = {
    method: "PUT",
    headers: {
      Authorization: `Bearer ${token}`,
      "Content-Length": 300,
      "Content-Range": `bytes 5500-5800/5801`,
    },
    body: chunkToSend,
  };
  const uResponse = await fetch(resumableURI, upload_options);
  const uploadResponse = await uResponse.text();

For other Google Drive API Examples: https://www.mikesallese.me/tags/google-drive-api-examples