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, for example, 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
Location: http://localhost:55001/plone/folder/@tus-upload/032803b64ad746b3ab46d9223ea3d90f
Tus-Resumable: 1.0.0

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 sent 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
Location: http://localhost:55001/plone/folder/document-2016-10-21
Tus-Resumable: 1.0.0
Upload-Offset: 8

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, telling the client the new offset:

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

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 temporarily created upload resource in the Location header of the response:

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

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
Location: http://localhost:55001/plone/myfile
Tus-Resumable: 1.0.0
Upload-Offset: 8

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
Tus-Resumable: 1.0.0
Upload-Length: 8
Upload-Offset: 3

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 extensions and the TUS version:

HTTP/1.1 204 No Content
Tus-Extension: creation,expiration
Tus-Resumable: 1.0.0
Tus-Version: 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,Lock-Token"
  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 https://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, for example TUS_TMP_FILE_DIR=/tmp/tus-uploads.