Protocole tus
tus est un protocole ouvert pour les téléchargements de fichiers reprenables. Utilisez-le pour des téléchargements fiables qui peuvent survivre aux interruptions de réseau.
Pourquoi tus ?
- Repreable : Continuez les téléchargements après les défaillances du réseau
- Efficace : Reprenez là où vous vous étiez arrêté, pas depuis le début
- Standardisé : Protocole bien défini avec de nombreuses implémentations serveur
- Fragmenté : Gère automatiquement les fichiers volumineux
Installation
npm install @samithahansaka/dropup tus-js-client
tus-js-client est une dépendance peer.
Utilisation de base
import { useDropup } from '@samithahansaka/dropup';
import { createTusUploader } from '@samithahansaka/dropup/tus';
function TusUploader() {
const { files, actions, state, getDropProps, getInputProps } = useDropup({
upload: createTusUploader({
endpoint: 'https://tusd.tusdemo.net/files/',
}),
onUploadComplete: (file) => {
console.log('Upload complete:', file.uploadedUrl);
},
});
return (
<div>
<div {...getDropProps()} style={styles.dropzone}>
<input {...getInputProps()} />
<p>Drop files for resumable upload</p>
</div>
{files.map(file => (
<div key={file.id} style={styles.fileItem}>
<span>{file.name}</span>
<span>{file.progress}%</span>
<span>{file.status}</span>
</div>
))}
<button onClick={() => actions.upload()}>
Upload
</button>
</div>
);
}
Options de configuration
createTusUploader({
// Requis
endpoint: 'https://your-tus-server.com/files/',
// Taille de fragment (par défaut : Infinity = pas de fragmentation)
chunkSize: 5 * 1024 * 1024, // Fragments de 5 Mo
// Configuration de réessai
retryDelays: [0, 1000, 3000, 5000], // Réessayer après 0s, 1s, 3s, 5s
// En-têtes pour toutes les requêtes
headers: {
'Authorization': 'Bearer token',
},
// Métadonnées envoyées au serveur
metadata: {
filename: 'file.name',
filetype: 'file.type',
// Ajouter des métadonnées personnalisées
userId: 'user123',
},
// Activer la reprise via localStorage
storeFingerprintForResuming: true,
// Supprimer l'empreinte après un téléchargement réussi
removeFingerprintOnSuccess: true,
// Téléchargements parallèles
parallelUploads: 3,
});
Reprendre les téléchargements précédents
tus peut reprendre les téléchargements interrompus :
const { files, actions } = useDropup({
upload: createTusUploader({
endpoint: 'https://your-tus-server.com/files/',
// Stocker les URL de téléchargement pour la reprise
storeFingerprintForResuming: true,
// Optionnel : clé de stockage personnalisée
urlStorageKey: 'my-app-tus-uploads',
onShouldRetry: (error, retryAttempt) => {
// Logique de réessai personnalisée
if (error.message.includes('network')) {
return retryAttempt < 5;
}
return false;
},
}),
});
// Les fichiers peuvent être repris automatiquement lorsqu'ils sont rajoutés
// ou manuellement avec retry :
actions.retry(['file-id']);
Suivi de la progression
function TusWithProgress() {
const { files, actions, getDropProps, getInputProps } = useDropup({
upload: createTusUploader({
endpoint: 'https://your-tus-server.com/files/',
}),
onUploadProgress: (file, progress) => {
console.log(`${file.name}: ${progress}%`);
},
onUploadStart: (file) => {
console.log(`Starting upload: ${file.name}`);
},
onUploadComplete: (file) => {
console.log(`Complete: ${file.uploadedUrl}`);
},
});
return (
<div>
<div {...getDropProps()}>
<input {...getInputProps()} />
<p>Drop files here</p>
</div>
{files.map(file => (
<div key={file.id}>
<span>{file.name}</span>
{file.status === 'uploading' && (
<div style={styles.progressBar}>
<div
style={{
...styles.progress,
width: `${file.progress}%`,
}}
/>
</div>
)}
<span>{file.status}</span>
</div>
))}
</div>
);
}
Pause et reprise
function PausableUploader() {
const { files, actions, getDropProps, getInputProps } = useDropup({
upload: createTusUploader({
endpoint: 'https://your-tus-server.com/files/',
storeFingerprintForResuming: true,
}),
});
return (
<div>
<div {...getDropProps()}>
<input {...getInputProps()} />
<p>Drop files here</p>
</div>
{files.map(file => (
<div key={file.id}>
<span>{file.name}</span>
<span>{file.progress}%</span>
{file.status === 'uploading' && (
<button onClick={() => actions.cancel(file.id)}>
Pause
</button>
)}
{file.status === 'paused' && (
<button onClick={() => actions.retry([file.id])}>
Resume
</button>
)}
{file.status === 'error' && (
<button onClick={() => actions.retry([file.id])}>
Retry
</button>
)}
</div>
))}
<button onClick={() => actions.upload()}>
Start All
</button>
</div>
);
}
Configuration du serveur
tusd (serveur officiel)
# Installer tusd
go install github.com/tus/tusd/cmd/tusd@latest
# Exécuter avec les options par défaut
tusd -upload-dir=/uploads
# Exécuter avec le stockage S3
tusd -s3-bucket=my-bucket -s3-endpoint=https://s3.amazonaws.com
Node.js (tus-node-server)
const { Server } = require('@tus/server');
const { FileStore } = require('@tus/file-store');
const server = new Server({
path: '/files',
datastore: new FileStore({ directory: './uploads' }),
});
// Avec Express
app.all('/files/*', server.handle.bind(server));
Gestion des métadonnées personnalisées
// Côté serveur : accéder aux métadonnées
const server = new Server({
path: '/files',
datastore: new FileStore({ directory: './uploads' }),
onUploadCreate: async (req, res, upload) => {
// Accéder aux métadonnées envoyées depuis le client
console.log(upload.metadata);
// { filename: 'photo.jpg', filetype: 'image/jpeg', userId: 'user123' }
},
onUploadFinish: async (req, res, upload) => {
// Traiter le téléchargement terminé
console.log('Upload complete:', upload.id);
},
});
Extensions tus
L'intégration tus de Dropup prend en charge ces extensions tus :
| Extension | Description |
|---|---|
| Creation | Créer de nouveaux téléchargements |
| Termination | Annuler les téléchargements |
| Checksum | Vérifier l'intégrité |
| Concatenation | Téléchargements parallèles |
| Expiration | Nettoyage automatique |
Gestion des erreurs
const { files, actions } = useDropup({
upload: createTusUploader({
endpoint: 'https://your-tus-server.com/files/',
retryDelays: [0, 1000, 3000, 5000, 10000],
}),
onUploadError: (file, error) => {
if (error.code === 'TUS_OFFLINE') {
console.log('Network offline, will resume when connected');
} else if (error.code === 'TUS_EXPIRED') {
console.log('Upload expired, starting fresh');
actions.retry([file.id]);
} else {
console.error('Upload failed:', error.message);
}
},
});
Téléchargements de fichiers volumineux
tus est idéal pour les fichiers volumineux :
function LargeFileUploader() {
const { files, actions, getDropProps, getInputProps } = useDropup({
maxSize: 10 * 1024 * 1024 * 1024, // Limite de 10 Go
upload: createTusUploader({
endpoint: 'https://your-tus-server.com/files/',
chunkSize: 50 * 1024 * 1024, // Fragments de 50 Mo
retryDelays: [0, 3000, 5000, 10000, 20000],
storeFingerprintForResuming: true,
}),
onUploadProgress: (file, progress) => {
// Afficher la progression détaillée pour les fichiers volumineux
const uploadedMB = (file.size * progress / 100 / 1024 / 1024).toFixed(0);
const totalMB = (file.size / 1024 / 1024).toFixed(0);
console.log(`${uploadedMB}MB / ${totalMB}MB`);
},
});
return (
<div {...getDropProps()}>
<input {...getInputProps()} />
<p>Upload large files (up to 10GB)</p>
</div>
);
}
Comparaison : tus vs téléchargement fragmenté
| Fonctionnalité | tus | Fragmenté personnalisé |
|---|---|---|
| Repreable | Intégré | Manuel |
| Support serveur | Nombreuses options | Personnalisé |
| Surcharge du protocole | Plus élevée | Plus faible |
| Configuration | Standardisée | Flexible |
| Idéal pour | Fichiers volumineux, réseaux peu fiables | Téléchargements simples |