The Drive API documentation is not very straight forward when it comes to uploading files when you don’t know the size of the file. In this post I’ll explain a way to do a chunked file upload when the file length is unknown.
The starting point for this post is the Google documentation for resumable upload.
The first things we’ll need to do is generate a folder id and initiate a resumable upload session. These steps are explained in my previous post about uploading files to Google Drive.
For initiating the resumable session, you don’t need to pass the X-Upload-Content-Length header to the request, because we don’t know the length.
Having steps 1 and 2 completed, as described in the link above, you should have the folder id and the resumable session URI. It should look like this in the HTTP response:
Location: https://www.googleapis.com/upload/drive/v2/files?uploadType=resumable&upload_id=AEnB2Ur95YVdDj76pT9qqQbGb7Lx9gnaidHs062SDIyVH4YNsCaTDXyQLC4xQavJsvxFN4rqBKpjyF0iIvYm4v7NGXY7kHZ6Kw
At this point we can start uploading data. We will start uploading data as chunks. All chunks must be multiples of 256 KB, except for the final chunk that will be smaller and will complete the upload (as per the Google documentation).
A first request will look like this:
PUT /upload/drive/v2/files?uploadType=resumable&upload_id=AEnB2Ur95YVdDj76pT9qqQbGb7Lx9gnaidHs062SDIyVH4YNsCaTDXyQLC4xQavJsvxFN4rqBKpjyF0iIvYm4v7NGXY7kHZ6Kw HTTP/1.1
Host: www.googleapis.com
Accept: */*
Depth: infinity
Content-Type: application/json; charset=UTF-8
Connection: Close
Authorization: Bearer ya29.AQJurm9AXfT2pOSHaL5VSgWNB5piqvzJz10CjOJ5VWiC-1LaO9my5zhYKgq3BLzebt38
Content-Range: bytes 0-2621439/*
Content-Length: 2621440
I’m sending chunks of 2560 KB in size.
The key is the Content-Range header which is made of the size interval of the chunks we are sending (0-2621439), followed by /* in case of intermediate requests.
Let’s see how the second request looks like:
PUT /upload/drive/v2/files?uploadType=resumable&upload_id=AEnB2Ur95YVdDj76pT9qqQbGb7Lx9gnaidHs062SDIyVH4YNsCaTDXyQLC4xQavJsvxFN4rqBKpjyF0iIvYm4v7NGXY7kHZ6Kw HTTP/1.1
Host: www.googleapis.com
Accept: */*
Depth: infinity
Content-Type: application/json; charset=UTF-8
Connection: Close
Authorization: Bearer ya29.AQJurm9AXfT2pOSHaL5VSgWNB5piqvzJz10CjOJ5VWiC-1LaO9my5zhYKgq3BLzebt38
Content-Range: bytes 2621440-5242879/*
Content-Length: 2621440
As you can see, the Content-Range continues the file upload from where we left off in the first request.
These requests will continue like this until we get to a file chunk that is smaller than 2560 KB (or whatever size you chose for your transfer). This will be the request that will complete the upload.
In case of the last request, we have to fill the Content-Range header with the actual final size of the file. The size of the file is easy to calculate at this point: number of intermediate requests * 2560 KB + size of the last chunk.
The last request will look like this:
PUT /upload/drive/v2/files?uploadType=resumable&upload_id=AEnB2Ur95YVdDj76pT9qqQbGb7Lx9gnaidHs062SDIyVH4YNsCaTDXyQLC4xQavJsvxFN4rqBKpjyF0iIvYm4v7NGXY7kHZ6Kw HTTP/1.1
Host: www.googleapis.com
Accept: */*
Depth: infinity
Content-Type: application/json; charset=UTF-8
Connection: Close
Authorization: Bearer ya29.AQJurm9AXfT2pOSHaL5VSgWNB5piqvzJz10CjOJ5VWiC-1LaO9my5zhYKgq3BLzebt38
Content-Range: bytes 20971520-22324961/22324962
Content-Length: 1353442
Observe the different Content-Range header.
If this last request succeeds, you will receive in the response, the uploaded file metadata in JSON format:
HTTP/1.1 200 OK
X-GUploader-UploadID: AEnB2Ur95YVdDj76pT9qqQbGb7Lx9gnaidHs062SDIyVH4YNsCaTDXyQLC4xQavJsvxFN4rqBKpjyF0iIvYm4v7NGXY7kHZ6Kw
ETag: "BhHHITEi3P331vdo-oAOUKP5GTI/MTQ0Mzg1NTcxMzk2Mw"
Vary: Origin
Vary: X-Origin
Content-Type: application/json; charset=UTF-8
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: Fri, 01 Jan 1990 00:00:00 GMT
Date: Sat, 03 Oct 2015 07:01:54 GMT
Content-Length: 2900
Server: UploadServer
Alternate-Protocol: 443:quic,p=1
Alt-Svc: quic=":443"; p="1"; ma=604800
Connection: close
{
"kind": "drive#file",
"id": "0B0jHrhwKFmtTUTRqV2ZLeWZvemc",
"etag": "\"BhHHITEi3P331vdo-oAOUKP5GTI/MTQ0Mzg1NTcxMzk2Mw\"",
"selfLink": "https://www.googleapis.com/drive/v2/files/0B0jHrhwKFmtTUTRqV2ZLeWZvemc",
"webContentLink": "https://docs.google.com/a/mios.com/uc?id=0B0jHrhwKFmtTUTRqV2ZLeWZvemc&export=download",
"alternateLink": "https://drive.google.com/a/mios.com/file/d/0B0jHrhwKFmtTUTRqV2ZLeWZvemc/view?usp=drivesdk",
"embedLink": "https://video.google.com/get_player?ps=docs&partnerid=30&docid=0B0jHrhwKFmtTUTRqV2ZLeWZvemc&BASE_URL=http://docs.google.com/",
"iconLink": "https://ssl.gstatic.com/docs/doclist/images/icon_11_video_list.png",
"title": "video_chunky5.mp4",
"mimeType": "video/mp4",
"labels": {
"starred": false,
"hidden": false,
"trashed": false,
"restricted": false,
"viewed": true
},
"createdDate": "2015-10-03T07:01:53.963Z",
"modifiedDate": "2015-10-03T07:01:53.963Z",
"modifiedByMeDate": "2015-10-03T07:01:53.963Z",
"lastViewedByMeDate": "2015-10-03T07:01:53.963Z",
"markedViewedByMeDate": "1970-01-01T00:00:00.000Z",
"version": "10405",
"parents": [
{
"kind": "drive#parentReference",
"id": "0B0jHrhwKFmtTa2dPdmVnNG4zX0U",
"selfLink": "https://www.googleapis.com/drive/v2/files/0B0jHrhwKFmtTUTRqV2ZLeWZvemc/parents/0B0jHrhwKFmtTa2dPdmVnNG4zX0U",
"parentLink": "https://www.googleapis.com/drive/v2/files/0B0jHrhwKFmtTa2dPdmVnNG4zX0U",
"isRoot": false
}
],
"downloadUrl": "https://doc-0k-8o-docs.googleusercontent.com/docs/securesc/4d9kohsgeit6ki75mpffvsab8q96ncdc/apd7nicq8gnku3dhfvvi92s2cm7cq4ib/1443852000000/15695070019414305924/15695070019414305924/0B0jHrhwKFmtTUTRqV2ZLeWZvemc?h=06506760665999687228&e=download&gd=true",
"userPermission": {
"kind": "drive#permission",
"etag": "\"BhHHITEi3P331vdo-oAOUKP5GTI/a3aNFad-eKHqFWQcWiyU_USyyNU\"",
"id": "me",
"selfLink": "https://www.googleapis.com/drive/v2/files/0B0jHrhwKFmtTUTRqV2ZLeWZvemc/permissions/me",
"role": "owner",
"type": "user"
},
"originalFilename": "video_chunky5.mp4",
"fileExtension": "mp4",
"md5Checksum": "b9d440339cbf0e0da432049ee5be4eb9",
"fileSize": "22324962",
"quotaBytesUsed": "22324962",
"ownerNames": [
"XXXX"
],
"owners": [
{
"kind": "drive#user",
"displayName": "XXXXX",
"isAuthenticatedUser": true,
"permissionId": "15695070019414305924",
"emailAddress": "XXXXX"
}
],
"lastModifyingUserName": "XXXXX",
"lastModifyingUser": {
"kind": "drive#user",
"displayName": "XXXXXX",
"isAuthenticatedUser": true,
"permissionId": "15695070019414305924",
"emailAddress": "XXXXXXXX"
},
"editable": true,
"copyable": true,
"writersCanShare": true,
"shared": false,
"explicitlyTrashed": false,
"appDataContents": false,
"headRevisionId": "0B0jHrhwKFmtTaEVOU3psTmVnQXdpMUlZSGh4dkRkK0tEUUZZPQ",
"spaces": [
"drive"
]
}