Tus
The @uppy/tus
plugin brings resumable file uploading with Tus
to Uppy by wrapping the tus-js-client
.
When should I use it?
Not sure which uploader is best for you? Read “Choosing the uploader you need”.
Tus is an open protocol for resumable uploads built on HTTP. This means accidentally closing your tab or losing connection let’s you continue, for instance, your 10GB upload instead of starting all over.
Tus supports any language, any platform, and any network. It requires a client and server integration to work. You can checkout the client and server implementations to find the server in your preferred language. You can store files on the Tus server itself, but you can also use service integrations (such as S3) to store files externally. If you don’t want to host your own server, see “Are there hosted Tus servers?”.
If you want reliable, resumable uploads: use @uppy/tus
to connect to your Tus
server in a few lines of code.
Install
- NPM
- Yarn
- CDN
npm install @uppy/tus
yarn add @uppy/tus
The bundle consists of most Uppy plugins, so this method is not recommended for production, as your users will have to download all plugins when you are likely using only a few.
It can be useful to speed up your development environment, so don't hesitate to use it to get you started.
<!-- 1. Add CSS to `<head>` -->
<link href="https://releases.transloadit.com/uppy/v4.7.0/uppy.min.css" rel="stylesheet">
<!-- 2. Initialize -->
<div id="uppy"></div>
<script type="module">
import { Uppy, Tus } from "https://releases.transloadit.com/uppy/v4.7.0/uppy.min.mjs"
new Uppy().use(Tus, { endpoint: 'https://tusd.tusdemo.net/files' })
</script>
Use
A quick overview of the complete API.
import Uppy from '@uppy/core';
import Dashboard from '@uppy/dashboard';
import Tus from '@uppy/tus';
import '@uppy/core/dist/style.min.css';
import '@uppy/dashboard/dist/style.min.css';
new Uppy()
.use(Dashboard, { inline: true, target: 'body' })
.use(Tus, { endpoint: 'https://tusd.tusdemo.net/files/' });
TypeScript
If you want the response
argument on the upload-success
event and
file.response.body
to be typed, you have to pass a generic to the Uppy class.
// ...
import Tus, { type TusBody } from '@uppy/tus';
type MyMeta = {
/* your added meta data */
};
const uppy = new Uppy<MyMeta, TusBody>().use(Tus, {
endpoint: 'https://tusd.tusdemo.net/files/',
});
const [firstFile] = uppy.getFiles();
// Correctly typed as XMLHttpRequest.
// Populated after uppy.upload()
firstFile.response.body.xhr;
API
Options
All options are passed to tus-js-client
and we document the ones here that are
required, added, or changed. This means you can also pass functions like
onAfterResponse
.
We recommended taking a look at the
API reference
from tus-js-client
to know what is supported.
id
A unique identifier for this plugin (string
, default: 'Tus'
).
endpoint
URL of the tus server (string
, default: null
).
headers
An object or function returning an object with HTTP headers to send along
requests (object | function
, default: null
).
Keys are header names, values are header values.
const headers = {
authorization: `Bearer ${window.getCurrentUserToken()}`,
};
Header values can also be derived from file data by providing a function. The function receives an Uppy file and must return an object where the keys are header names, and values are header values.
const headers = (file) => {
return {
authorization: `Bearer ${window.getCurrentUserToken()}`,
expires: file.meta.expires,
};
};
chunkSize
A number indicating the maximum size of a PATCH
request body in bytes
(number
, default: Infinity
). Note that this option only affects local
browser uploads. If you need a max chunk size for remote (Companion) uploads,
you must set the chunkSize
Companion option as well.
Do not set this value unless you are forced to. The two valid reasons are
described in the
tus-js-client
docs.
withCredentials
Configure the requests to send Cookies using the
xhr.withCredentials
property (boolean
, default: false
).
The remote server must accept CORS and credentials.
retryDelays
When uploading a chunk fails, automatically try again after the defined
millisecond intervals (Array<number>
, default: [0, 1000, 3000, 5000]
).
By default, we first retry instantly; if that fails, we retry after 1 second; if that fails, we retry after 3 seconds, etc.
Set to null
to disable automatic retries, and fail instantly if any chunk
fails to upload.
onBeforeRequest(req, file)
Behaves like the
onBeforeRequest
function from tus-js-client
but with the added file
argument.
onShouldRetry: (err, retryAttempt, options, next)
When an upload fails onShouldRetry
is called with the error and the default
retry logic as the last argument (function
).
The default retry logic is an exponential backoff algorithm triggered on HTTP 429 (Too Many Requests) errors. Meaning if your server (or proxy) returns HTTP 429 because it’s being overloaded, @uppy/tus will find the ideal sweet spot to keep uploading without overloading.
If you want to extend this functionality, for instance to retry on unauthorized requests (to retrieve a new authentication token):
import Uppy from '@uppy/core';
import Tus from '@uppy/tus';
new Uppy().use(Tus, {
endpoint: '',
async onBeforeRequest(req) {
const token = await getAuthToken();
req.setHeader('Authorization', `Bearer ${token}`);
},
onShouldRetry(err, retryAttempt, options, next) {
if (err?.originalResponse?.getStatus() === 401) {
return true;
}
return next(err);
},
async onAfterResponse(req, res) {
if (res.getStatus() === 401) {
await refreshAuthToken();
}
},
});
allowedMetaFields
Pass an array of field names to limit the metadata fields that will be added to
uploads as
Tus Metadata
(Array
, default: null
).
- Set it to
false
to not send any fields (or an empty array). - Set it to
['name']
to only send thename
field. - Set it to
true
(the default) to send all metadata fields.
limit
Limit the amount of uploads going on at the same time (number
, default: 20
).
Setting this to 0
means no limit on concurrent uploads (not recommended).
Frequently Asked Questions
The Tus website has extensive FAQ section, we recommend taking a look there as well if something is unclear.
How is file meta data stored?
Tus uses unique identifiers for the file names to prevent naming collisions. To
still keep the meta data in place, Tus also uploads an extra .info
file with
the original file name and other meta data:
{
"ID": "00007a99d16d4eeb5a3e3c080b6f69da+JHZavdqPSK4VMtarg2yYcNiP8t_kDjN51lBYMJdEyr_wqEotVl8ZBRBSTnWKWenZBwHvbLNz5tQXYp2N7Vdol.04ysQAuw__suTJ4IsCljj0rjyWA6LvV4IwF5P2oom2",
"Size": 1679852,
"SizeIsDeferred": false,
"Offset": 0,
"MetaData": {
"filename": "cat.jpg",
"filetype": "image/jpeg"
},
"IsPartial": false,
"IsFinal": false,
"PartialUploads": null,
"Storage": {
"Bucket": "your-bucket",
"Key": "some-key",
"Type": "s3store"
}
}
How do I change files before sending them?
If you want to change the file names, you want to do that in
onBeforeFileAdded
.
If you want to send extra headers with the request, use headers
or
onBeforeRequest
.
How do I change (or move) files after sending them?
If you want to preserve files names, extract meta data, or move files to a
different place you generally can with hooks or events. It depends on the Tus
server you use how it’s done exactly. tusd
, for instance, exposes
hooks and
tus-node-server
has
events.
Which server do you recommend?
Transloadit runs tusd
in production, where it
serves millions of requests globally. So we recommend tusd
as battle-tested
from our side, but other companies have had success with other
implementations so it depends on your needs.
Are there hosted Tus servers?
All Transloadit plans come with a hosted
tusd
server. You don’t have to do anything to leverage it, using
@uppy/transloadit
automatically uses Tus under the hood.
Why Tus instead of directly uploading to AWS S3?
First: reliable, resumable uploads. This means accidentally closing your tab or losing connection let’s you continue, for instance, your 10GB upload instead of starting all over.
Tus is also efficient with lots of files (such as 8K) and large files. Uploading to AWS S3 directly from the client also introduces quite a bit of overhead, as more requests are needed for the flow to work.