0
# Upload Handlers
1
2
Upload handler plugins manage the actual file upload process, supporting various protocols and destinations including resumable uploads, traditional HTTP uploads, and cloud storage services.
3
4
## Capabilities
5
6
### Resumable Uploads
7
8
Protocols that support resumable uploads for reliability and performance.
9
10
```typescript { .api }
11
/**
12
* Tus resumable upload protocol implementation
13
*/
14
class Tus<M extends Meta = {}, B extends Body = {}> extends BasePlugin<TusOptions> {
15
constructor(uppy: Uppy<M, B>, options?: TusOptions);
16
}
17
18
interface TusOptions {
19
endpoint: string;
20
headers?: { [key: string]: string };
21
chunkSize?: number;
22
withCredentials?: boolean;
23
overridePatchMethod?: boolean;
24
retryDelays?: number[];
25
parallelUploads?: number;
26
removeFingerprintOnSuccess?: boolean;
27
uploadDataDuringCreation?: boolean;
28
urlStorage?: TusUrlStorage;
29
fileReader?: TusFileReader;
30
onBeforeRequest?: (req: HttpRequest) => void;
31
onAfterResponse?: (req: HttpRequest, res: HttpResponse) => void;
32
limit?: number;
33
onShouldRetry?: (err: Error, retryAttempt: number, options: TusOptions) => boolean;
34
metaFields?: string[];
35
allowedMetaFields?: string[];
36
locale?: Locale;
37
}
38
39
interface TusUrlStorage {
40
findAllUploads(): Promise<{ [key: string]: string }>;
41
findUploadsByFingerprint(fingerprint: string): Promise<string[]>;
42
removeUpload(urlStorageKey: string): Promise<void>;
43
addUpload(fingerprint: string, upload: { uploadUrl: string; urlStorageKey: string }): Promise<void>;
44
}
45
46
interface TusFileReader {
47
slice(file: Blob, start: number, end: number): Blob;
48
}
49
```
50
51
**Usage Example:**
52
53
```typescript
54
import { Uppy, Tus } from "uppy";
55
56
const uppy = new Uppy()
57
.use(Tus, {
58
endpoint: 'https://tusd.tusdemo.net/files/',
59
chunkSize: 1024 * 1024, // 1MB chunks
60
retryDelays: [0, 1000, 3000, 5000],
61
parallelUploads: 3,
62
removeFingerprintOnSuccess: true,
63
headers: {
64
'Authorization': 'Bearer ' + authToken
65
},
66
onBeforeRequest: (req) => {
67
// Modify request before sending
68
req.setHeader('X-Custom-Header', 'value');
69
},
70
onAfterResponse: (req, res) => {
71
// Handle response
72
console.log('Upload progress response:', res.getStatus());
73
}
74
});
75
```
76
77
### Traditional HTTP Uploads
78
79
Standard HTTP upload implementations using XMLHttpRequest or fetch.
80
81
```typescript { .api }
82
/**
83
* XHR upload implementation for standard HTTP endpoints
84
*/
85
class XHRUpload<M extends Meta = {}, B extends Body = {}> extends BasePlugin<XHRUploadOptions<M, B>> {
86
constructor(uppy: Uppy<M, B>, options?: XHRUploadOptions<M, B>);
87
}
88
89
interface XHRUploadOptions<M extends Meta = {}, B extends Body = {}> {
90
endpoint: string;
91
method?: 'POST' | 'PUT' | 'PATCH';
92
formData?: boolean;
93
fieldName?: string;
94
allowedMetaFields?: string[];
95
headers?: { [key: string]: string } | ((file: UppyFile<M, B>) => { [key: string]: string });
96
bundle?: boolean;
97
withCredentials?: boolean;
98
timeout?: number;
99
limit?: number;
100
responseType?: '' | 'text' | 'json' | 'blob' | 'document' | 'arraybuffer';
101
responseUrlFieldName?: string;
102
getResponseData?: (responseText: string, response: XMLHttpRequest) => B;
103
getResponseError?: (responseText: string, response: XMLHttpRequest) => Error;
104
onBeforeRequest?: (xhr: XMLHttpRequest, file: UppyFile<M, B>) => void;
105
validateStatus?: (status: number, responseText: string, response: XMLHttpRequest) => boolean;
106
locale?: Locale;
107
}
108
```
109
110
**Usage Examples:**
111
112
```typescript
113
// Standard form data upload
114
uppy.use(XHRUpload, {
115
endpoint: '/api/upload',
116
method: 'POST',
117
formData: true,
118
fieldName: 'file',
119
headers: {
120
'X-CSRF-Token': csrfToken
121
},
122
allowedMetaFields: ['name', 'caption', 'description'],
123
limit: 5, // Max 5 concurrent uploads
124
timeout: 30000, // 30 second timeout
125
126
onBeforeRequest: (xhr, file) => {
127
// Add file-specific headers
128
xhr.setRequestHeader('X-File-Name', file.name);
129
xhr.setRequestHeader('X-File-Size', file.size.toString());
130
},
131
132
getResponseData: (responseText, response) => {
133
// Parse custom response format
134
return JSON.parse(responseText);
135
},
136
137
validateStatus: (status, responseText, response) => {
138
// Custom success validation
139
return status >= 200 && status < 300;
140
}
141
});
142
143
// JSON upload with custom headers per file
144
uppy.use(XHRUpload, {
145
endpoint: '/api/files',
146
method: 'PUT',
147
formData: false,
148
headers: (file) => ({
149
'Content-Type': file.type,
150
'X-File-ID': file.id,
151
'Authorization': `Bearer ${getTokenForFile(file)}`
152
}),
153
responseType: 'json'
154
});
155
```
156
157
### Cloud Storage Direct Upload
158
159
Direct upload to cloud storage services bypassing your server.
160
161
```typescript { .api }
162
/**
163
* AWS S3 direct upload implementation
164
*/
165
class AwsS3<M extends Meta = {}, B extends Body = {}> extends BasePlugin<AwsS3Options> {
166
constructor(uppy: Uppy<M, B>, options?: AwsS3Options);
167
}
168
169
interface AwsS3Options {
170
companionUrl?: string;
171
companionHeaders?: { [key: string]: string };
172
companionCookiesRule?: string;
173
getUploadParameters?: (file: UppyFile<any, any>) => Promise<S3UploadParameters> | S3UploadParameters;
174
timeout?: number;
175
limit?: number;
176
metaFields?: string[];
177
allowedMetaFields?: string[];
178
locale?: Locale;
179
}
180
181
interface S3UploadParameters {
182
method?: 'POST' | 'PUT';
183
url: string;
184
fields?: { [key: string]: string };
185
headers?: { [key: string]: string };
186
}
187
```
188
189
**Usage Examples:**
190
191
```typescript
192
// S3 upload with companion server
193
uppy.use(AwsS3, {
194
companionUrl: 'https://companion.uppy.io',
195
limit: 3,
196
timeout: 60000
197
});
198
199
// S3 upload with custom parameters
200
uppy.use(AwsS3, {
201
getUploadParameters: async (file) => {
202
// Get signed URL from your server
203
const response = await fetch('/api/s3-params', {
204
method: 'POST',
205
headers: {
206
'Content-Type': 'application/json'
207
},
208
body: JSON.stringify({
209
filename: file.name,
210
contentType: file.type,
211
size: file.size
212
})
213
});
214
215
const data = await response.json();
216
217
return {
218
method: 'POST',
219
url: data.url,
220
fields: data.fields,
221
headers: data.headers
222
};
223
},
224
225
allowedMetaFields: ['caption', 'description'],
226
limit: 5
227
});
228
```
229
230
### Processing Services
231
232
Upload handlers that integrate with file processing and encoding services.
233
234
```typescript { .api }
235
/**
236
* Transloadit encoding service integration
237
*/
238
class Transloadit<M extends Meta = {}, B extends Body = {}> extends BasePlugin<TransloaditOptions> {
239
constructor(uppy: Uppy<M, B>, options?: TransloaditOptions);
240
}
241
242
interface TransloaditOptions {
243
assemblyOptions?: AssemblyOptions | ((file: UppyFile<M, B>) => AssemblyOptions);
244
waitForEncoding?: boolean;
245
waitForMetadata?: boolean;
246
alwaysRunAssembly?: boolean;
247
importFromUploadURLs?: boolean;
248
signature?: string;
249
params?: AssemblyParameters | ((file: UppyFile<M, B>) => AssemblyParameters);
250
fields?: { [key: string]: string } | ((file: UppyFile<M, B>) => { [key: string]: string });
251
limit?: number;
252
locale?: Locale;
253
}
254
255
interface AssemblyOptions {
256
params: AssemblyParameters;
257
signature?: string;
258
fields?: { [key: string]: string };
259
}
260
261
interface AssemblyParameters {
262
auth: {
263
key: string;
264
expires?: string;
265
};
266
template_id?: string;
267
steps?: { [key: string]: StepOptions };
268
notify_url?: string;
269
}
270
271
interface StepOptions {
272
robot: string;
273
use?: string | string[];
274
[key: string]: any;
275
}
276
```
277
278
**Usage Example:**
279
280
```typescript
281
// Transloadit with template
282
uppy.use(Transloadit, {
283
params: {
284
auth: { key: 'YOUR_TRANSLOADIT_KEY' },
285
template_id: 'YOUR_TEMPLATE_ID'
286
},
287
waitForEncoding: true,
288
289
// Dynamic assembly options per file
290
assemblyOptions: (file) => ({
291
params: {
292
auth: { key: 'YOUR_TRANSLOADIT_KEY' },
293
steps: {
294
resize: {
295
robot: '/image/resize',
296
width: file.meta.targetWidth || 800,
297
height: file.meta.targetHeight || 600,
298
resize_strategy: 'fit'
299
},
300
optimize: {
301
robot: '/image/optimize',
302
use: 'resize'
303
}
304
}
305
}
306
})
307
});
308
```
309
310
## Upload Handler Events
311
312
```typescript { .api }
313
// Upload lifecycle events
314
interface UploadHandlerEvents<M extends Meta = {}, B extends Body = {}> {
315
'upload-started': (file: UppyFile<M, B>) => void;
316
'upload-progress': (file: UppyFile<M, B>, progress: FileProgress) => void;
317
'upload-success': (file: UppyFile<M, B>, response: UploadSuccessResponse<B>) => void;
318
'upload-error': (file: UppyFile<M, B>, error: Error, response?: UploadErrorResponse<M, B>) => void;
319
'upload-retry': (file: UppyFile<M, B>) => void;
320
}
321
322
// Tus-specific events
323
interface TusEvents<M extends Meta = {}, B extends Body = {}> {
324
'tus:upload-created': (file: UppyFile<M, B>, upload: { uploadURL: string }) => void;
325
'tus:chunk-uploaded': (file: UppyFile<M, B>, chunk: { chunkNumber: number, chunkSize: number }) => void;
326
}
327
328
// S3-specific events
329
interface AwsS3Events<M extends Meta = {}, B extends Body = {}> {
330
's3:upload-created': (file: UppyFile<M, B>, upload: S3Upload) => void;
331
's3:upload-progress': (file: UppyFile<M, B>, progress: S3Progress) => void;
332
}
333
```
334
335
## Response Types
336
337
```typescript { .api }
338
interface UploadSuccessResponse<B extends Body = {}> {
339
status: number;
340
body: B;
341
uploadURL?: string;
342
}
343
344
interface UploadErrorResponse<M extends Meta = {}, B extends Body = {}> {
345
status: number;
346
body: any;
347
message?: string;
348
details?: string;
349
}
350
351
interface HttpRequest {
352
getMethod(): string;
353
getURL(): string;
354
setHeader(name: string, value: string): void;
355
getHeader(name: string): string | undefined;
356
setProgressHandler(handler: (bytesSent: number, bytesTotal: number) => void): void;
357
}
358
359
interface HttpResponse {
360
getStatus(): number;
361
getHeader(name: string): string | undefined;
362
getBody(): string;
363
}
364
```