Resumable uploads

This page describes how to make a resumable upload request to the Google Photos Library API via the REST protocol. This protocol allows you to resume an upload operation after a communication failure interrupts the flow of data.

If you are a developer using client libraries, note that some client libraries provide native support for resumable uploads.

Use the resumable upload option if:

  • You are uploading large files.
  • The likelihood of network interruption or some other transmission failure is high (for example, if you are uploading a file from a mobile app).

Resumable uploads can also reduce your bandwidth usage when there is a network failure, because you don't have to restart large file uploads from the beginning.

Step 1: Initiating an upload session

Initiate a resumable upload session by sending a POST request to https://photoslibrary.googleapis.com/v1/uploads. Using the resumable upload URL returned in this request, upload the file.

The POST request must include the following headers:

Header fields
Content-Length Set to 0 as the request body is empty.
X-Goog-Upload-Command Set to start.
X-Goog-Upload-Content-Type Set to the mime type of the file, for example, image/jpeg.
X-Goog-Upload-Protocol Set to resumable.
X-Goog-Upload-Raw-Size Set to the total number of bytes of the file data to be transferred.

Here is a POST request header:

POST https://photoslibrary.googleapis.com/v1/uploads
Authorization: Bearer oauth2-token
Content-Length: 0
X-Goog-Upload-Command: start
X-Goog-Upload-Content-Type: mime-type
X-Goog-Upload-Protocol: resumable
X-Goog-Upload-Raw-Size: bytes-of-file

Step 2: Saving the session URL

If successful, the POST request returns a 200 OK HTTP status code, including the following header.

X-Goog-Upload-URL: url-to-make-uploads-to
X-Goog-Upload-Chunk-Granularity: chunk-granularity-in-bytes

The header field x-goog-upload-chunk-granularity contains the byte alignment and size granularity for all data chunks sent by the client. If the upload is done in multiple chunks, all uploads, except the last upload, must be done in multiples of this value. That is, the upload bytes of the file must be aligned to this value. In the last chunk, you can upload the remaining bytes.

The header field X-Goog-Upload-URL contains a unique URL that must be used to complete the upload through all of the remaining requests. Copy and save this resumable session URL, so that you can use it for subsequent requests.

Step 3: Uploading the file

There are two ways to upload a file with a resumable session:

  1. In a single request. This approach is usually the best, because it requires fewer requests, and thus has better performance.
  2. In multiple chunks. In this approach, uploads are made in multiple requests by chunking the data. The data is chunked in multiples of x-goog-upload-chunk-granularity. If necessary, the chunked requests can be re-tried.

    Use this approach if:

    • You need to reduce the amount of data transferred in any single request. You might need to do this when there is a fixed time limit for individual requests.
    • You need to provide a customized indicator showing the upload progress.
    • You need to know when it is safe to discard data.

Single Request

To upload the file in a single request:

  1. Create a POST request to the resumable session URL.
  2. Add the file's data to the request body.
  3. Add the following HTTP headers:

    • Content-Length: Set to the number of bytes in the file.
    • X-Goog-Upload-Command: Set to upload, finalize.
  4. Send the request.

If the upload request is interrupted or you receive a 5xx response, follow the procedure in Resuming an interrupted upload.

If the request succeeds, you receive a 200 OK HTTP status code and an upload token in the response body. Create the media item using this upload token.

Multiple Chunks

To upload the file in multiple chunks:

  1. Create a POST request to the resumable session URL.
  2. Add the chunk's data to the request body.

    Except for the final chunk that completes the upload, create the other chunks in multiples of the accepted size of chunks. Keep the chunk size as large as possible so that the upload is efficient.

  3. Add the following HTTP headers:

    • Content-Length: Set to the number of bytes in the chunk.
    • X-Goog-Upload-Command: Set to upload. For the last chunk, set to upload, finalize.
    • X-Goog-Upload-Offset: Set to the offset at which the bytes should be written. Note that the bytes must be uploaded serially. The first offset is 0.
  4. Send the request.

    If the upload request is interrupted or you receive a 5xx response, follow the procedure in Resuming an interrupted upload.

  5. Repeat the above steps for each remaining chunk in the file.

If the request succeeds, you receive a 200 OK HTTP status code and an upload token in the response body. Create the media item using this upload token.

Example

Single Request

The following example shows a resumable request to upload a 3,039,417-byte JPEG file in a single request.

POST https://photoslibrary.googleapis.com/v1/uploads HTTP/1.1
Content-Length: 0
X-Goog-Upload-Command: start
X-Goog-Upload-Content-Type: image/jpeg
X-Goog-Upload-Protocol: resumable
X-Goog-Upload-Raw-Size: 3039417
[no body]

The response contains the upload URL and the expected chunk size:

HTTP/1.1 200 OK
X-Goog-Upload-URL: https://photoslibrary.googleapis.com/v1/uploads?upload_id=AEnB2Urq&upload_protocol=resumable
X-Goog-Upload-Chunk-Granularity: 262144

The final upload request:

POST https://photoslibrary.googleapis.com/v1/uploads?upload_id=AEnB2Urq&upload_protocol=resumable HTTP/1.1
Content-Length: 3039417
X-Goog-Upload-Command: upload, finalize
X-Goog-Upload-Offset: 0

[BYTES 0-4199999]

Multiple Chunks

The following example shows a resumable request to upload a 3,039,417-byte JPEG file in multiple chunks, using the resumable session URL and the accepted chunk size granularity obtained in the previous step. This example uses a chunk size of 262,144 bytes which was returned in the header field, x-goog-upload-chunk-granularity, when the upload session was initialized. Note that each upload contains bytes that are in multiples of 262,144.

Initialize the upload session to receive the upload URL and chunk size as described in the previous step:

POST https://photoslibrary.googleapis.com/v1/uploads HTTP/1.1
Content-Length: 0
X-Goog-Upload-Command: start
X-Goog-Upload-Content-Type: image/jpeg
X-Goog-Upload-Protocol: resumable
X-Goog-Upload-Raw-Size: 3039417
[no body]

The response contains the upload URL and the expected chunk size:

HTTP/1.1 200 OK
X-Goog-Upload-URL: https://photoslibrary.googleapis.com/v1/uploads?upload_id=AEnB2Urq&upload_protocol=resumable
X-Goog-Upload-Chunk-Granularity: 262144

First chunk:

POST https://photoslibrary.googleapis.com/v1/uploads?upload_id=AEnB2Urq&upload_protocol=resumable HTTP/1.1
Content-Length: 1048576
X-Goog-Upload-Command: upload
X-Goog-Upload-Offset: 0

[BYTES 0-1048575]

Second chunk:

POST https://photoslibrary.googleapis.com/v1/uploads?upload_id=AEnB2Urq&upload_protocol=resumable HTTP/1.1
Content-Length: 1048576
X-Goog-Upload-Command: upload
X-Goog-Upload-Offset: 1048576

[BYTES 1048576-2097151]

Last chunk:

POST https://photoslibrary.googleapis.com/v1/uploads?upload_id=AEnB2Urq&upload_protocol=resumable HTTP/1.1
Content-Length: 942265
X-Goog-Upload-Command: upload, finalize
X-Goog-Upload-Offset: 2097152

[BYTES 2097152-4200000]

Resuming an interrupted upload

If the upload request is interrupted or if you receive a non-200 HTTP status code, query the server to find out how much of the upload succeeded.

Here is a POST request to the resumable session URL. X-Goog-Upload-Command should be set to query.

POST https://photoslibrary.googleapis.com/v1/uploads?upload_id=AEnB2Urq&upload_protocol=resumable HTTP/1.1
Content-Length: 0
X-Goog-Upload-Command: query

The response from the server includes a 200 OK HTTP status code and the current size of the upload.

HTTP/1.1 200 OK
X-Goog-Upload-Status: active
X-Goog-Upload-Size-Received: 100

You can then resume uploading at this offset. You must resume at the offset provided by the server unless you send a combined upload and finalize command, in which case you can also resume at offset 0.

If the X-Goog-Upload-Status header in the HTTP response of your query command is present and the value is not active, that indicates that the upload has already been terminated.