Go back to index

SRIORestUploadBundle

Handle multiple upload ways on your Symfony2 REST API

To upload data files more reliably, you can use the resumable upload protocol. This protocol allows you to resume an upload operation after a communication failure has interrupted the flow of data. It is especially useful if you are transferring large files and the likelihood of a network interruption or some other transmission failure is high, for example, when uploading from a mobile client app. It can also reduce your bandwidth usage in the event of network failures because you don't have to restart large file uploads from the beginning.

The steps for using resumable upload include:

  1. Start a resumable session. Make an initial request to the upload URI that includes the metadata, if any.
  2. Save the resumable session URI. Save the session URI returned in the response of the initial request; you'll use it for the remaining requests in this session.
  3. Upload the file. Send the media file to the resumable session URI.

In addition, apps that use resumable upload need to have code to resume an interrupted upload. If an upload is interrupted, find out how much data was successfully received, and then resume the upload starting from that point.

Start a resumable session

For this initiating request, the body is either empty or it contains the metadata only; you'll transfer the actual contents of the file you want to upload in subsequent requests.

Use the following HTTP headers with the initial request:

  • X-Upload-Content-Type. Set to the media MIME type of the upload data to be transferred in subsequent requests.
  • X-Upload-Content-Length. Set to the number of bytes of upload data to be transferred in subsequent requests.
  • Content-Type. Set according to the metadata's data type.
  • Content-Length. Set to the number of bytes provided in the body of this initial request.

The following example shows the use of a resumable upload request for an upload path that would be /upload:

POST /upload?uploadType=resumable HTTP/1.1
Host: www.example.com
Content-Length: 41
Content-Type: application/json; charset=UTF-8
X-Upload-Content-Type: image/jpeg
X-Upload-Content-Length: 2000000

{
    "name": "Some value"
}

Save the resumable session URI

If the session initiation request succeeds, the API server responds with a 200 OK HTTP status code. In addition, it provides a Location header that specifies your resumable session URI. The Location header, shown in the example below, includes an uploadId query parameter portion that gives the unique upload ID to use for this session.

Here is the response to the request in the last step:

HTTP/1.1 200 OK
Location: /upload?uploadType=resumable&uploadId=fooBar123
Content-Length: 0

The value of the Location header, as shown in the above example response, is the session URI you'll use as the HTTP endpoint for doing the actual file upload or querying the upload status.

Upload the file

To upload the file, send a PUT request to the upload URI that you obtained in the previous step.

The HTTP headers to use when making the resumable file upload requests includes Content-Length. Set this to the number of bytes you are uploading in this request, which is generally the upload file size.

PUT /upload?uploadType=resumable&uploadId=fooBar123 HTTP/1.1
Content-Length: 2000000
Content-Type: image/jpeg

bytes 0-1999999

If the request succeeds, the server responds with an HTTP 201 Created, along with any metadata associated with this resource. If the initial request of the resumable session had been a PUT, to update an existing resource, the success response would be 200 OK, along with any metadata associated with this resource.

Upload file in chunks

With resumable uploads, you can break a file into chunks and send a series of requests to upload each chunk in sequence. This is not the preferred approach since there are performance costs associated with the additional requests, and it is generally not needed. However, you might need to use chunking to reduce the amount of data transferred in any single request.

If you are uploading the data in chunks, the Content-Range header is also required, along with the Content-Length header required for full file uploads:

  • Content-Length. Set to the chunk size or possibly less, as might be the case for the last request.
  • Content-Range: Set to show which bytes in the file you are uploading. For example, Content-Range: bytes 0-524287/2000000 shows that you are providing the first 524,288 bytes in a 2,000,000 byte file.

A sample request would be:

PUT {session_uri} HTTP/1.1
Host: www.example.com
Content-Length: 524288
Content-Type: image/jpeg
Content-Range: bytes 0-524287/2000000

bytes 0-524288

If the request succeeds, the server responds with 308 Resume Incomplete, along with a Range header that identifies the total number of bytes that have been stored so far:

HTTP/1.1 308 Resume Incomplete
Content-Length: 0
Range: 0-524287

Use the upper value returned in the Range header to determine where to start the next chunk. Continue to PUT each chunk of the file until the entire file has been uploaded

Resume an interrupted upload

If an upload request is terminated before receiving a response or if you receive an HTTP 503 Service Unavailable or even an HTTP 500 Internal Server Error response from the server, then you need to resume the interrupted upload. To do this:

  1. Request the upload status

    PUT {session_uri} HTTP/1.1
    Content-Length: 0
    Content-Range: bytes */2000000
  2. Extract the number of bytes uploaded so far from the response

    The server's response uses the Range header to indicate that it has received the first 43 bytes of the file so far. Use the upper value of the Range header to determine where to start the resumed upload.

    HTTP/1.1 308 Resume Incomplete
    Content-Length: 0
    Range: 0-42
  3. Resume the upload from the point where it left off

    Use the upload file in chunks method to restart upload at the point where there's a failure.