Upload a file in chunks via resumable upload with the Google Drive API.
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:
- Send the initial request and retrieve the resumable session URI.
- Upload the data and monitor upload state.
- (optional) If upload is disturbed, resume the upload.
We’re going to cover the following steps:
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
}
};
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
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