This is a summary of currently supported upload ways.
- Simple: Send binary data to an URL and use query parameters to submit additional data.
- Multipart: Send both JSON and binary data using the multipart Content-Type.
- FormData: Matches the classic browser file upload
- Resumable: start a resumable upload session by sending JSON data and then send file entirely or by chunks. It allow to restart a failed upload where it stops.
The most straightforward method for uploading a file is by making a simple upload request. This option is a good choice when:
- The file is small enough to upload again in its entirety if the connection fails
- There is no or a very small amount of metadata to send
To use simple upload, make a POST
or PUT
request to the upload method's URI and add the query parameter uploadType=simple
.
The HTTP headers to use when making a simple upload request include:
Content-Type
. Set the media content-typeContent-Length
. Set to the number of bytes you are uploading.
The following example shows the use of a simple photo upload request for an upload path that would be /upload
:
POST /upload?uploadType=simple HTTP/1.1
Host: www.example.com
Content-Type: image/jpeg
Content-Length: number_of_bytes_in_JPEG_file
JPEG data
If you have metadata that you want to send along with the data to upload, you can make a single multipart/related
request. This is a good choice if the data you are sending is small enough to upload again in its entirety if the connection fails.
To use multipart upload, make a POST
or PUT
request to the upload method's URI and add the query parameter uploadType=multipart
.
The top-level HTTP headers to use when making a multipart upload request include:
Content-Type
. Set tomultipart/related
and include the boundary string you're using to identify the parts of the request.Content-Length
. Set to the total number of bytes in the request body.
The body of the request is formatted as a multipart/related
content type [RFC2387] and contains exactly two parts. The parts are identified by a boundary string, and the final boundary string is followed by two hyphens.
Each part of the multipart request needs an additional Content-Type
header:
- Metadata part: Must come first, and Content-Type must match one of the the accepted metadata formats.
- Media part: Must come second, and Content-Type must match one the method's accepted media MIME types.
The following example shows the use of a multipart upload request for an upload path that would be /upload
:
POST /upload?uploadType=multipart HTTP/1.1
Host: www.example.com
Content-Type: multipart/related; boundary="foo_bar_baz"
Content-Length: number_of_bytes_in_entire_request_body
--foo_bar_baz
Content-Type: application/json; charset=UTF-8
{
"name": "Some value"
}
--foo_bar_baz
Content-Type: image/jpeg
JPEG data
--foo_bar_baz--
This may be the most used way to upload files: it matches with the classic form "file" upload.
You just have to have a field of type file
named file
on your form and set the action path to /upload?uploadType=formData
.
It can ether be used with any XHR upload method.
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:
- Start a resumable session. Make an initial request to the upload URI that includes the metadata, if any.
- 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.
- 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.
First, you need to configure the bundle.
The entity will contains the resumable upload sessions and is required if you want the resumable way of upload to work.
<?php
namespace Acme\Entity;
use SRIO\RestUploadBundle\Entity\ResumableUploadSession as BaseResumableUploadSession;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class ResumableUploadSession extends BaseResumableUploadSession
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
}
Copy this entity in your bundle and update the schema.
Configure the bundle to use this entity to store resumable upload sessions:
srio_rest_upload:
resumable_entity_class: Acme\Entity\ResumableUploadSession
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"
}
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.
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.
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
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:
-
Request the upload status
PUT {session_uri} HTTP/1.1 Content-Length: 0 Content-Range: bytes */2000000
-
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 theRange
header to determine where to start the resumed upload.HTTP/1.1 308 Resume Incomplete Content-Length: 0 Range: 0-42
-
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.