Perform resumable uploads

Overview

This page describes how to make a resumable upload request in theCloud Storage JSON and XML APIs. This protocol lets you resume an uploadoperation after a communication failure interrupts the flow of data.

For information on using resumable uploads in the Google Cloud CLI and clientlibraries, seeHow tools and APIs use resumable uploads.

Required roles

To get the permissions that you need to perform a resumable upload,ask your administrator to grant you one of the following roles:

  • For uploads that include anObject Retention Lock, ask youradministrator to grant you the Storage Object Admin(roles/storage.objectAdmin) IAM role for the bucket.

  • For all other cases, ask your administrator to grant you the Storage ObjectUser (roles/storage.objectUser) IAM role for the bucket.

These predefined roles contain the permissions required to upload an object to abucket for their respective cases. To see the exact permissions that arerequired, expand theRequired permissions section:

Required permissions

  • storage.objects.create
  • storage.objects.delete
    • This permission is only required for uploads that overwrite an existingobject.
  • storage.objects.setRetention
    • This permission is only required for uploads that include an ObjectRetention Lock.

You can also get these permissions with otherpredefined roles orcustom roles.

For information about granting roles on buckets, seeSet and manage IAM policies on buckets.

Initiate a resumable upload session

To initiate a resumable upload session:

JSON API

  1. Have gcloud CLIinstalled and initialized, which lets you generate an access token for theAuthorization header.

  2. Optionally, create a JSON file that contains themetadata youwant to set on the object that you're uploading. For example, thefollowing JSON file sets thecontentType metadata of the object youwant to upload toimage/png:

    {    "contentType": "image/png"}
  3. UsecURL to call theJSON API with aPOST Object request:

    curl -i -X POST --data-binary @METADATA_LOCATION \    -H "Authorization: Bearer $(gcloud auth print-access-token)" \    -H "Content-Type: application/json" \    -H "Content-Length:INITIAL_REQUEST_LENGTH" \    "https://storage.googleapis.com/upload/storage/v1/b/BUCKET_NAME/o?uploadType=resumable&name=OBJECT_NAME"

    Where:

    • METADATA_LOCATION is the local path to theJSON file containing the optional metadata you specified in theprevious step. If you are not including a metadata file, excludethis, along with--data-binary @ and theContent-Type header.
    • INITIAL_REQUEST_LENGTH is the number ofbytes in the body of this initial request, for example79.
    • BUCKET_NAME is the name of the bucket towhich you are uploading your object. For example,my-bucket.
    • OBJECT_NAME is the URL-encoded name you wantto give your object. For example,pets/dog.png, URL-encoded aspets%2Fdog.png. This is not required if you included aname inthe object metadata file in Step 2.

    If you have enabledCross-Origin Resource Sharing, you should alsoinclude anOrigin header in both this and subsequent upload requests.

    Optional headers that you can add to the request includeX-Upload-Content-Type andX-Upload-Content-Length.

    If successful, the response includes a200 status code and lookssimilar to the following:

    HTTP/2 200content-type: text/plain; charset=utf-8x-guploader-uploadid: ABgVH8_jqDHM_KOvNAJCx73r9v5fINktk9U-pXana1szCM5803tlJ7CKsRbDxkyYCrfEiNqzcZ6B7DfoDtc-bdzpH-SpVTAMEO9EQV34qG0-0yklocation: https://storage.googleapis.com/upload/storage/v1/b/my-bucket/o?uploadType=resumable&name=cat-pic.jpeg&upload_id=ABgVH8_jqDHM_KOvNAJCx73r9v5fINktk9U-pXana1szCM5803tlJ7CKsRbDxkyYCrfEiNqzcZ6B7DfoDtc-bdzpH-SpVTAMEO9EQV34qG0-0ykdate: Mon, 07 Jul 2025 14:57:21 GMTvary: Originvary: X-Origincache-control: no-cache, no-store, max-age=0, must-revalidateexpires: Mon, 01 Jan 1990 00:00:00 GMTpragma: no-cachecontent-length: 0server: UploadServeralt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
  4. Save the resumablesession URI given in thelocation header ofthe response to yourPOST Object request.

    This URI is used in subsequent requests to upload the object data.

    Caution: Be careful when sharing the resumable session URI, because itcan be used by anyone to upload data to the target bucket without anyfurther authentication.

XML API

  1. Have gcloud CLIinstalled and initialized, which lets you generate an access token for theAuthorization header.

  2. UsecURL to call theXML API with aPOST Object request that has an empty body:

    curl -i -X POST -H "Authorization: Bearer $(gcloud auth print-access-token)" \    -H "Content-Length: 0" \    -H "Content-Type:OBJECT_CONTENT_TYPE" \    -H "x-goog-resumable: start" \    "https://storage.googleapis.com/BUCKET_NAME/OBJECT_NAME"

    Where:

    • OBJECT_CONTENT_TYPE is thecontent type of the object. For example,image/png. If you do not specify a content type, the Cloud Storage system sets theContent-Type metadata for the object to beapplication/octet-stream.
    • BUCKET_NAME is the name of the bucket to which you are uploading your object. For example,my-bucket.
    • OBJECT_NAME is the URL-encoded name you want to give your object. For example,pets/dog.png, URL-encoded aspets%2Fdog.png.

    If you have enabledCross-Origin Resource Sharing, you should alsoinclude anOrigin header in both this and subsequent upload requests.

    If successful, the response includes a201 status message.

  3. Save the resumablesession URI given in thelocation headerof the response to yourPOST Object request.

    This URI is used in subsequent requests to upload the object data.

    Caution: Be careful when sharing the resumable session URI, because itcan be used by anyone to upload data to the target bucket without anyfurther authentication.

Upload the data

Once you have initiated a resumable upload, there are two ways to upload theobject's data:

Single chunk upload

To upload the data in a single chunk:

JSON API

  1. UsecURL to call theJSON API with aPUTObject request:

    curl -i -X PUT --data-binary @OBJECT_LOCATION \    -H "Content-Length:OBJECT_SIZE" \    "SESSION_URI"

    Where:

    • OBJECT_LOCATION is the local path to yourobject. For example,Desktop/dog.png.
    • OBJECT_SIZE is the number of bytes in yourobject. For example,20000000.
    • SESSION_URI is the value returned in thelocation header when youinitiated the resumable upload.

    Optionally, you can include headers prefixed withX-Goog-Meta-to add custom metadata for the object as part of this request.

XML API

  1. UsecURL to call theXML API with aPUTObject request:

    curl -i -X PUT --data-binary @OBJECT_LOCATION \    -H "Content-Length:OBJECT_SIZE" \    "SESSION_URI"

    Where:

    • OBJECT_LOCATION is the local path to yourobject. For example,Desktop/dog.png.
    • OBJECT_SIZE is the number of bytes in yourobject. For example,20000000.
    • SESSION_URI is the value returned in theLocation header when youinitiated the resumable upload.

If the upload completes in its entirety, you receive a200 OK or201 Createdresponse, along with any metadata associated with the resource.

If the upload request is interrupted, or if you receive a5xxresponse, follow the procedure inResuming an interrupted upload.

Multiple chunk upload

To upload the data in multiple chunks:

JSON API

  1. Create a chunk of data from the overall data you want to upload.

    The chunk size should be a multiple of 256 KiB (256 x 1024 bytes),unless it's the last chunk that completes the upload. Larger chunk sizestypically make uploads faster, but note that there's a tradeoff betweenspeed and memory usage. It's recommended that you use at least 8 MiB forthe chunk size.

  2. UsecURL to call theJSON API with aPUTObject request:

    curl -i -X PUT --data-binary @CHUNK_LOCATION \    -H "Content-Length:CHUNK_SIZE" \    -H "Content-Range: bytesCHUNK_FIRST_BYTE-CHUNK_LAST_BYTE/TOTAL_OBJECT_SIZE" \    "SESSION_URI"

    Where:

    • CHUNK_LOCATION is the local path to the chunkthat you're currently uploading.
    • CHUNK_SIZE is the number of bytes you'reuploading in the current request. For example,8388608.
    • CHUNK_FIRST_BYTE is the starting byte in theoverall object that the chunk you're uploading contains.
    • CHUNK_LAST_BYTE is the ending byte in theoverall object that the chunk you're uploading contains.
    • TOTAL_OBJECT_SIZE is the total size of theobject you're uploading. If you don't know the full size of your object, use*for this value.
    • SESSION_URI is the value returned in theLocation header when youinitiated the resumable upload.

    An exampleContent-Range isContent-Range: bytes 0-8388607/20000000.SeeContent-Range for more information about this header.

    If the request succeeds, the server responds with308 Resume Incomplete. The response contains aRange header.

  3. Repeat the above steps for each remaining chunk of data that you want toupload, using the upper value contained in theRange header of eachresponse to determine where to start each successive chunk; you shouldnot assume that the server received all bytes sent in any given request.

    Optionally, in the final request of the resumable upload you can includeheaders prefixed withX-Goog-Meta- to add custom metadata forthe object.

XML API

  1. Create a chunk of data from the overall data you want to upload.

    The chunk size should be a multiple of 256 KiB (256 x 1024 bytes),unless it's the last chunk that completes the upload. Larger chunk sizestypically make uploads faster, but note that there's a tradeoff betweenspeed and memory usage. It's recommended that you use at least 8 MiB forthe chunk size.

  2. UsecURL to call theXML API with aPUTObject request:

    curl -i -X PUT --data-binary @CHUNK_LOCATION \    -H "Content-Length:CHUNK_SIZE" \    -H "Content-Range: bytesCHUNK_FIRST_BYTE-CHUNK_LAST_BYTE/TOTAL_OBJECT_SIZE" \    "SESSION_URI"

    Where:

    • CHUNK_LOCATION is the local path to the chunkthat you're currently uploading.
    • CHUNK_SIZE is the number of bytes you'reuploading in the current request. For example,8388608.
    • CHUNK_FIRST_BYTE is the starting byte in theoverall object that the chunk you're uploading contains.
    • CHUNK_LAST_BYTE is the ending byte in theoverall object that the chunk you're uploading contains.
    • TOTAL_OBJECT_SIZE is the total size of theobject you're uploading. If you don't know the full size of your object, use*for this value.
    • SESSION_URI is the value returned in theLocation header when youinitiated the resumable upload.

    An exampleContent-Range isContent-Range: bytes 0-8388607/20000000.SeeContent-Range for more information about this header.

    If the request succeeds, the server responds with308 Resume Incomplete. The response contains aRange header.

  3. Repeat the above steps for each remaining chunk of data that you want toupload, using the upper value contained in theRange header of eachresponse to determine where to start each successive chunk; you shouldnot assume that the server received all bytes sent in any given request.

Once the upload completes in its entirety, you receive a200 OK or201 Created response, along with any metadata associated with the resource.

If any of the chunk uploads are interrupted, or if you receive a5xxresponse, you should either resend the interrupted chunk in its entirety orresume the interrupted upload from where it left off.

Check the status of a resumable upload

If your resumable upload is interrupted, or you're not sure the uploadcompleted, you can check the status of the upload:

JSON API

  1. UsecURL to call theJSON API with aPUTObject request:

    curl -i -X PUT \    -H "Content-Length: 0" \    -H "Content-Range: bytes */OBJECT_SIZE" \    "SESSION_URI"

    Where:

    • OBJECT_SIZE is the total number of bytes inyour object. If you don't know the full size of your object, use*for this value.
    • SESSION_URI is the value returned in theLocation header when youinitiated the resumable upload.

XML API

  1. UsecURL to call theXML API with aPUTObject request:

    curl -i -X PUT \    -H "Content-Length: 0" \    -H "Content-Range: bytes */OBJECT_SIZE" \    "SESSION_URI"

    Where:

    • OBJECT_SIZE is the total number of bytes inyour object. If you don't know the full size of your object, use*for this value.
    • SESSION_URI is the value returned in theLocation header when youinitiated the resumable upload.

A200 OK or201 Created response indicates that the upload wascompleted, and no further action is necessary.

A308 Resume Incomplete response indicates that you need to continueuploading the data.

  • If Cloud Storage has not yet persisted any bytes, the308 responsedoes not have aRange header. In this case, you should start yourupload from the beginning.
  • Otherwise, the308 response has aRange header, which specifieswhich bytes Cloud Storage has persisted so far. Use this value whenresuming an interrupted upload.

Resume an interrupted upload

Important: Cloud Storageignores any bytes you send at an offsetthat Cloud Storagehas already persisted.

If an upload request is terminated before receiving a response, or if youreceive a503 or500 response, then you need to resumethe interrupted upload from where it left off. To resume an interrupted upload:

JSON API

  1. Check the status of your resumable upload.

  2. Save the upper value of theRange header contained in the responseto your status check. If the response does not have aRange header,Cloud Storage has not yet persisted any bytes, and you shouldupload from the beginning.

  3. Ensure that that object data you're about to upload begins at the bytefollowing the upper value in theRange header.

  4. If the interrupted request contained headers prefixed withX-Goog-Meta-, include those headers in the request to resumeyour upload.

  5. UsecURL to call theJSON API with aPUTObject request that picks up at the byte following the value in theRange header:

    curl -i -X PUT --data-binary @PARTIAL_OBJECT_LOCATION \    -H "Content-Length:UPLOAD_SIZE_REMAINING" \    -H "Content-Range: bytesNEXT_BYTE-LAST_BYTE/TOTAL_OBJECT_SIZE" \    "SESSION_URI"

    Where:

    • PARTIAL_OBJECT_LOCATION is the local path tothe remaining portion of data that you want to upload.
    • UPLOAD_SIZE_REMAINING is the number of bytesyou're uploading in the current request. For example, uploading therest of an object with a total size of 20000000 that was interruptedafter bytes 0-42 uploaded would have anUPLOAD_SIZE_REMAINING of19999957.
    • NEXT_BYTE is the next integer after the valueyou saved in step 2. For example, if42 is the upper value instep 2, the value forNEXT_BYTE is43.
    • LAST_BYTE is the ending byte contained in thisPUT request. For example, to finish uploading an object whose totalsize is20000000, the value forLAST_BYTEis19999999.
    • TOTAL_OBJECT_SIZE is the total size of theobject you're uploading. For example,20000000. If you don't know the full size of your object, use*for this value.
    • SESSION_URI is the value returned in theLocation header when youinitiated the resumable upload.

XML API

  1. Check the status of your resumable upload.

  2. Save the upper value of theRange header contained in theresponse to your status check. If the response does not have aRangeheader, Cloud Storage has not yet persisted any bytes, and youshouldupload from the beginning.

  3. Ensure that that object data you're about to upload begins at the bytefollowing the upper value in theRange header.

  4. UsecURL to call theXML API with aPUT Objectrequest that picks up at the byte following the value in theRangeheader:

    curl -i -X PUT --data-binary @PARTIAL_OBJECT_LOCATION \    -H "Content-Length:UPLOAD_SIZE_REMAINING" \    -H "Content-Range: bytesNEXT_BYTE-LAST_BYTE/TOTAL_OBJECT_SIZE" \    "SESSION_URI"

    Where:

    • PARTIAL_OBJECT_LOCATION is the local path tothe remaining portion of data that you want to upload.
    • UPLOAD_SIZE_REMAINING is the number of bytesyou're uploading in the current request. For example, uploading therest of an object with a total size of 20000000 that was interruptedafter bytes 0-42 uploaded would have anUPLOAD_SIZE_REMAINING of19999957.
    • NEXT_BYTE is the next integer after the valueyou saved in step 2. For example, if42 is the upper value instep 2, the value forNEXT_BYTE is43.
    • LAST_BYTE is the ending byte contained in thisPUT request. For example, to finish uploading an object whose totalsize is20000000, the value forLAST_BYTEis19999999.
    • TOTAL_OBJECT_SIZE is the total size ofthe object you're uploading. For example,20000000. If youdon't know the full size of your object, use* for this value.
    • SESSION_URI is the value returned in theLocation header when youinitiated the resumable upload.

You can resume uploads as many times as necessary while the session URI isactive; the session URI expires after a week. When the data is successfullyuploaded, Cloud Storage responds with a200 OK or201 createdstatus code.

Cancel an upload

To cancel an incomplete resumable upload and prevent any further action on it:

JSON API

  1. UsecURL to call theJSON API with aDELETErequest:

    curl -i -X DELETE -H "Content-Length: 0" \  "SESSION_URI"

    Where:

If successful, the response contains a499 status code. Future attemptsto query or resume the upload result in a4xx response.

XML API

  1. UsecURL to call theXML API with aDELETErequest:

    curl -i -X DELETE -H "Content-Length: 0" \  "SESSION_URI"

    Where:

If successful, the response contains a204 status code, and futureattempts to query or resume the upload also result in a204 response.

Failure Handling

Under rare circumstances, a request to resume an interrupted upload might failwith a non-retriable '4xx' error because permissions on the bucket have changed,or because the integrity check on the final uploaded object detected a mismatch.If this occurs, retry the upload byinitiating a new resumable uploadsession.

What's next

Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2026-02-19 UTC.