Skip to main content

Uppy 4.0 is here: TypeScript rewrite, Google Photos, React hooks, and much more.

XHR

The @uppy/xhr-upload plugin is for regular uploads to a HTTP server.

When should I use it?

tip

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 install @uppy/xhr-upload

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 the name 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,
};
};
note

The function syntax is not available when bundle is set to true.

note

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.

caution

When bundle is set to true:

  • formData must also be set to true.
  • 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) => void | Promise<void>).

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`