TUS resumable upload

plone.restapi supports the TUS Open Protocol for resumable file uploads. There is a @tus-upload endpoint to upload a file and a @tus-replace endpoint to replace an existing file.

Creating an Upload URL

Note

POST requests to the @tus-upload endpoint are allowed on all IFolderish content types (e.g. Folder).

To create a new upload, send a POST request to the @tus-upload endpoint.

http

POST /plone/folder/@tus-upload HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
Tus-Resumable: 1.0.0
Upload-Length: 8
Upload-Metadata: filename dGVzdC50eHQ=,content-type dGV4dC9wbGFpbg==

curl

curl -i -X POST http://nohost/plone/folder/@tus-upload -H 'Accept: application/json' -H 'Tus-Resumable: 1.0.0' -H 'Upload-Length: 8' -H 'Upload-Metadata: filename dGVzdC50eHQ=,content-type dGV4dC9wbGFpbg==' --user admin:secret

httpie

http POST http://nohost/plone/folder/@tus-upload Accept:application/json Tus-Resumable:1.0.0 Upload-Length:8 Upload-Metadata:'filename dGVzdC50eHQ=,content-type dGV4dC9wbGFpbg==' -a admin:secret

python-requests

requests.post('http://nohost/plone/folder/@tus-upload', headers={
    'Accept': 'application/json',
    'Tus-Resumable': '1.0.0',
    'Upload-Length': '8',
    'Upload-Metadata': 'filename dGVzdC50eHQ=,content-type dGV4dC9wbGFpbg==',
}, auth=('admin', 'secret'))

The server will return a temporary upload URL in the location header of the response:

HTTP/1.1 201 Created
Tus-Resumable: 1.0.0
Location: http://localhost:55001/plone/folder/@tus-upload/032803b64ad746b3ab46d9223ea3d90f

The file can then be uploaded in the next step to that temporary URL.

Uploading a File

Note

PATCH requests to the @tus-upload endpoint are allowed on all IContentish content types.

Once a temporary upload URL has been created, a client can send a PATCH request to upload a file. The file content should be send in the body of the request:

PATCH /plone/folder/@tus-upload/032803b64ad746b3ab46d9223ea3d90f HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
Tus-Resumable: 1.0.0
Upload-Offset: 3
Content-Type: application/offset+octet-stream

defgh

When just a single file is uploaded at once, the server will respond with a 204: No Content response after a successful upload. The HTTP location header contains he URL of the newly created content object:

HTTP/1.1 204 No Content
Upload-Offset: 8
Location: http://localhost:55001/plone/folder/document-2016-10-21
Tus-Resumable: 1.0.0

Partial Upload

TUS allows partial upload of files. A partial file is also uploaded by sending a PATCH request to the temporary URL:

PATCH /plone/folder/@tus-upload/032803b64ad746b3ab46d9223ea3d90f HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
Tus-Resumable: 1.0.0
Upload-Offset: 0
Content-Type: application/offset+octet-stream

abc

The server will also respond with a 204: No content response. Though, instead of providing the final file URL in the ‘location’ header, the server provides an updated ‘Upload-Offset’ value, to tell the client the new offset:

HTTP/1.1 204 No Content
Upload-Offset: 3
Tus-Resumable: 1.0.0

When the last partial file has been uploaded, the server will contain the final file URL in the ‘location’ header.

Replacing Existing Files

TUS can also be used to replace an existing file by sending a POST request to the @tus-replace endpoint instead.

POST /plone/myfile/@tus-replace HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
Tus-Resumable: 1.0.0
Upload-Length: 8
Upload-Metadata: filename dGVzdC50eHQ=,content-type dGV4dC9wbGFpbg==

The server will respond with a 201: Created status and return the URL of the temprorary created upload resource in the location header of the response:

HTTP/1.1 201 Created
Tus-Resumable: 1.0.0
Location: http://localhost:55001/plone/folder/@tus-upload/032803b64ad746b3ab46d9223ea3d90f

The file can then be uploaded to that URL using the PATCH method in the same way as creating a new file:

PATCH /plone/myfile/@tus-upload/4e465958b24a46ec8657e6f3be720991 HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
Tus-Resumable: 1.0.0
Upload-Offset: 0
Content-Type: application/offset+octet-stream

abcdefgh

The server will respond with a 204: No Content response and the final file URL in the HTTP location header:

HTTP/1.1 204 No Content
Upload-Offset: 8
Location: http://localhost:55001/plone/myfile
Tus-Resumable: 1.0.0

Asking for the Current File Offset

To ask the server for the current file offset, the client can send a HEAD request to the upload URL:

http

HEAD /plone/folder/@tus-upload/032803b64ad746b3ab46d9223ea3d90f HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
Tus-Resumable: 1.0.0

curl

curl -i -X HEAD http://nohost/plone/folder/@tus-upload/032803b64ad746b3ab46d9223ea3d90f -H 'Accept: application/json' -H 'Tus-Resumable: 1.0.0' --user admin:secret

httpie

http HEAD http://nohost/plone/folder/@tus-upload/032803b64ad746b3ab46d9223ea3d90f Accept:application/json Tus-Resumable:1.0.0 -a admin:secret

python-requests

requests.head('http://nohost/plone/folder/@tus-upload/032803b64ad746b3ab46d9223ea3d90f', headers={
    'Accept': 'application/json',
    'Tus-Resumable': '1.0.0',
}, auth=('admin', 'secret'))

The server will respond with a 200: Ok status and the current file offset in the ‘Upload-Offset’ header:

HTTP/1.1 200 OK
Upload-Offset: 3
Upload-Length: 8
Tus-Resumable: 1.0.0

Configuration and Options

The current TUS configuration and a list of supported options can be retrieved sending an OPTIONS request to the @tus-upload endpoint:

http

OPTIONS /plone/folder/@tus-upload HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0

curl

curl -i -X OPTIONS http://nohost/plone/folder/@tus-upload -H 'Accept: application/json' --user admin:secret

httpie

http OPTIONS http://nohost/plone/folder/@tus-upload Accept:application/json -a admin:secret

python-requests

requests.options('http://nohost/plone/folder/@tus-upload', headers={
    'Accept': 'application/json',
}, auth=('admin', 'secret'))

The server will respond with a 204: No content status and HTTP headers containing information about the available extentions and the TUS version:

HTTP/1.1 204 No Content
Tus-Version: 1.0.0
Tus-Extension: creation,expiration
Tus-Resumable: 1.0.0

CORS Configuration

If you use CORS and want to make it work with TUS, you have to make sure the TUS specific HTTP headers are allowed by your CORS policy.

<plone:CORSPolicy
  allow_origin="http://localhost"
  allow_methods="DELETE,GET,OPTIONS,PATCH,POST,PUT"
  allow_credentials="true"
  allow_headers="Accept,Authorization,Origin,X-Requested-With,Content-Type,Upload-Length,Upload-Offset,Tus-Resumable,Upload-Metadata"
  expose_headers="Upload-Offset,Location,Upload-Length,Tus-Version,Tus-Resumable,Tus-Max-Size,Tus-Extension,Upload-Metadata"
  max_age="3600"
  />

See the plone.rest documentation for more information on how to configure CORS policies.

See http://tus.io/protocols/resumable-upload.html#headers for a list and description of the individual headers.

Temporary Upload Directory

During upload files are stored in a temporary directory that by default is located in the CLIENT_HOME directory. If you are using a multi ZEO client setup without session stickiness you must configure this to a directory shared by all ZEO clients by setting the TUS_TMP_FILE_DIR environment variable. E.g. TUS_TMP_FILE_DIR=/tmp/tus-uploads