XHR
The @uppy/xhr-upload
plugin is for regular uploads to a HTTP server.
When should I use it?
Not sure which uploader is best for you? Read “Choosing the uploader you need”.
When you have an existing HTTP server and you don’t need Transloadit services or want to run a tus server. Note that it’s still possible to use tus without running an extra server by integrating tus into your existing one. For instance, if you have a Node.js server (or server-side framework like Next.js) you could integrate tus-node-server.
Install
- NPM
- Yarn
- CDN
npm install @uppy/xhr-upload
yarn add @uppy/xhr-upload
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.8.0/uppy.min.css" rel="stylesheet">
<!-- 2. Initialize -->
<div id="uppy"></div>
<script type="module">
import { Uppy, XHRUpload } from "https://releases.transloadit.com/uppy/v4.8.0/uppy.min.mjs"
new Uppy().use(XHRUpload, { 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 XHR from '@uppy/xhr-upload';
import '@uppy/core/dist/style.min.css';
import '@uppy/dashboard/dist/style.min.css';
new Uppy()
.use(Dashboard, { inline: true, target: 'body' })
.use(XHR, { endpoint: 'https://your-domain.com/upload' });
API
Options
id
A unique identifier for this plugin (string
, default: 'XHRUpload'
).
endpoint
URL of the HTTP server (string
, default: null
).
method
Configures which HTTP method to use for the upload (string
, default:
'POST'
).
formData
Configures whether to use a multipart form upload, using FormData
(boolean
, default: true
).
This works similarly to using a <form>
element with an <input type="file">
for uploads. When set to true
, file metadata is also sent to the endpoint as
separate form fields. When set to false
, only the file contents are sent.
fieldName
When formData
is set to true, this is used as the form field
name for the file to be uploaded.
It defaults to 'files[]'
if bundle
option is set to true
, otherwise it
defaults to 'file'
.
allowedMetaFields
Pass an array of field names to limit the metadata fields that will be added to upload.
- 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.
If the formData
option is set to false, metaFields
is
ignored.
headers
An object containing HTTP headers to use for the upload request. 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,
};
};
The function syntax is not available when bundle
is set to true
.
Failed requests are retried with the same headers. If you want to change the
headers on retry,
such as refreshing an auth token,
you can use onBeforeRequest
.
bundle
Send all files in a single multipart request (boolean
, default: false
).
All files will be appended to the provided fieldName
field in the request.
When bundle
is set to true
:
formData
must also be set totrue
.- Uppy won’t be able to bundle remote files (such as Google Drive) and will throw an error in this case.
- Only global uppy metadata is sent to the endpoint. Individual per-file metadata is ignored.
To upload files on different fields, use
uppy.setFileState()
to set the
xhrUpload.fieldName
property on the file:
uppy.setFileState(fileID, {
xhrUpload: { fieldName: 'pic0' },
});
timeout: 30 * 1000
Abort the connection if no upload progress events have been received for this
milliseconds amount (number
, default: 30_000
).
Note that unlike the XMLHttpRequest.timeout
property, this is a
timer between progress events: the total upload can take longer than this value.
Set to 0
to disable this check.
limit
The maximum amount of files to upload in parallel (number
, default: 5
).
responseType
The response type expected from the server, determining how the xhr.response
property should be filled (string
, default: 'text'
).
The xhr.response
property can be accessed in a custom
getResponseData()
callback. This
option sets the XMLHttpRequest.responseType
property. Only
''
, 'text'
, 'arraybuffer'
, 'blob'
and 'document'
are widely supported
by browsers, so it’s recommended to use one of those.
withCredentials
Indicates whether cross-site Access-Control requests should be made using
credentials (boolean
, default: false
).
onBeforeRequest
An optional function that will be called before a HTTP request is sent out
((xhr: XMLHttpRequest, retryCount: number, files: UppyFile<M, B>[]) => void | Promise<void>
).
The third argument, files
, is an array of all Uppy files when bundle
is
true
. When false
, it only contains one file.
shouldRetry
An optional function called once an error appears and before retrying
((xhr: XMLHttpRequesT) => boolean
).
The amount of retries is 3, even if you continue to return true
. The default
behavior uses
exponential backoff with a
maximum of 3 retries.
onAfterResponse
An optional function that will be called after a HTTP response has been received
((xhr: XMLHttpRequest, retryCount: number) => void | Promise<void>
).
locale: {}
export default {
strings: {
// Shown in the Informer if an upload is being canceled because it stalled for too long.
timedOut: 'Upload stalled for %{seconds} seconds, aborting.',
},
};
getResponseData
An optional function to turn your non-JSON response into JSON with a url
property pointing to the uploaded file
((xhr: XMLHttpRequest) => { url: string }
).
You can also return properties other than url
and they will end up in
file.response.body
. If you do, make sure to set the Body
generic on Uppy
to make it typesafe when using TS.
Frequently Asked Questions
How can I refresh auth tokens after they expire?
import Uppy from '@uppy/core';
import XHR from '@uppy/xhr-upload';
let token = null;
async function getAuthToken() {
const res = await fetch('/auth/token');
const json = await res.json();
return json.token;
}
new Uppy().use(XHR, {
endpoint: '<your-endpoint>',
// Called again for every retry too.
async onBeforeRequest(xhr) {
if (!token) {
token = await getAuthToken();
}
xhr.setRequestHeader('Authorization', `Bearer ${token}`);
},
async onAfterResponse(xhr) {
if (xhr.status === 401) {
token = await getAuthToken();
}
},
});
How to send along meta data with the upload?
When using XHRUpload with formData: true
, file metadata is
sent along with each upload request. You can set metadata for a file using
uppy.setFileMeta(fileID, data)
, or
for all files simultaneously using
uppy.setMeta(data)
.
It may be useful to set metadata depending on some file properties, such as the
size. You can use the file-added
event and the
uppy.setFileMeta(fileID, data)
method to do this:
uppy.on('file-added', (file) => {
uppy.setFileMeta(file.id, {
size: file.size,
});
});
Now, a form field named size
will be sent along to the
endpoint
once the upload starts.
By default, all metadata is sent, including Uppy’s default name
and type
metadata. If you do not want the name
and type
metadata properties to be
sent to your upload endpoint, you can use the metaFields
option to restrict the field names that should be sent.
uppy.use(XHRUpload, {
// Only send our own `size` metadata field.
allowedMetaFields: ['size'],
});
How to upload to a PHP server?
The XHRUpload plugin works similarly to a <form>
upload. You can use the
$_FILES
variable on the server to work with uploaded files. See the PHP
documentation on Handling file uploads.
The default form field for file uploads is files[]
, which means you have to
access the $_FILES
array as described in Uploading many files:
<?php
// upload.php
$files = $_FILES['files'];
$file_path = $files['tmp_name'][0]; // temporary upload path of the first file
$file_name = $_POST['name']; // desired name of the file
move_uploaded_file($file_path, './img/' . basename($file_name)); // save the file in `img/`
Note how we are using $_POST['name']
instead of $my_file['name']
.
$my_file['name']
has the original name of the file on the user’s device.
$_POST['name']
has the name
metadata value for the uploaded file, which can
be edited by the user using the Dashboard.
Set a custom fieldName
to make working with the $_FILES
array a bit less
convoluted:
// app.js
uppy.use(XHRUpload, {
endpoint: '/upload.php',
fieldName: 'my_file',
});
<?php
// upload.php
$my_file = $_FILES['my_file'];
$file_path = $my_file['tmp_name']; // temporary upload path of the file
$file_name = $_POST['name']; // desired name of the file
move_uploaded_file($file_path, $_SERVER['DOCUMENT_ROOT'] . '/img/' . basename($file_name)); // save the file at `img/FILE_NAME`