
TUS — это открытый протокол для возобновляемой загрузки файлов поверх HTTP. Он решает главную проблему классической загрузки: если соединение оборвалось на середине, загрузку можно продолжить с того места, где она остановилась, а не начинать заново.
Протокол разработан как открытый стандарт и имеет реализации для множества языков и платформ.
Стандартная загрузка через <input type="file"> и FormData работает по принципу «всё или ничего»:
TUS решает эти проблемы через чанкирование (разбиение файла на части) и возобновляемость.
POST запрос с метаданными файла, сервер возвращает уникальный URL для загрузкиPATCH запросыHEAD запрос и продолжает с нужного местаРассмотрим практический пример — хук для загрузки видео с прогрессом и возможностью отмены.
npm install tus-js-client
import { useState } from 'react';
import { getVideoUploadUrl } from '../api';
import * as tus from 'tus-js-client';
const DEFAULT_CHUNK_SIZE = 5 * 1024 * 1024; // 5 МБ
export const useVideoUploader = () => {
const [progress, setProgress] = useState(0);
const [upload, setUpload] = useState<tus.Upload>();
const handleAbort = () => {
if (!upload) return;
upload.abort();
};
const handleVideoUpload = async (
video: File,
options?: {
onError?: () => void;
onSuccess?: (videoId: string) => void;
chunkSize?: number
},
) => {
try {
// Получаем URL для загрузки от нашего API
const { proxyUploadURL, id } = await getVideoUploadUrl(video.size);
const chunkSize = options?.chunkSize || DEFAULT_CHUNK_SIZE;
const upload = new tus.Upload(video, {
endpoint: proxyUploadURL,
// Задержки между повторными попытками при ошибке
retryDelays: [0, 3000, 5000, 10000, 20000],
metadata: {
filename: video.name,
filetype: video.type,
},
uploadSize: video.size,
chunkSize: chunkSize,
onError: options?.onError,
onProgress: function (bytesUploaded, bytesTotal) {
const percentage = Number(
((bytesUploaded / bytesTotal) * 100).toFixed(2)
);
setProgress(percentage);
},
onSuccess: () => options?.onSuccess?.(id),
});
setUpload(upload);
// Проверяем, есть ли предыдущие незавершённые загрузки
upload.findPreviousUploads().then(function (previousUploads) {
if (previousUploads.length) {
// Возобновляем с последней точки
upload.resumeFromPreviousUpload(previousUploads[0]);
}
upload.start();
});
} catch (e) {
console.error('Ошибка загрузки:', e);
}
};
return {
uploadVideo: handleVideoUpload,
uploadingProgress: progress,
abortUpload: handleAbort,
};
};
retryDelays: [0, 3000, 5000, 10000, 20000]
Массив задержек в миллисекундах между повторными попытками. При ошибке сети библиотека автоматически повторит запрос: сначала сразу, потом через 3 секунды, затем через 5, 10 и 20. После исчерпания попыток вызывается onError.
chunkSize: 5 * 1024 * 1024 // 5 МБ
Размер одного чанка. Меньшие чанки = чаще сохраняется прогресс, но больше HTTP-запросов. Для видео 5-10 МБ — хороший компромисс.
upload.findPreviousUploads().then(function (previousUploads) {
if (previousUploads.length) {
upload.resumeFromPreviousUpload(previousUploads[0]);
}
upload.start();
});
Библиотека хранит информацию о незавершённых загрузках в localStorage. Это позволяет возобновить загрузку даже после перезагрузки страницы.
const VideoUploadForm = () => {
const { uploadVideo, uploadingProgress, abortUpload } = useVideoUploader();
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
uploadVideo(file, {
onSuccess: (videoId) => {
console.log('Видео загружено! ID:', videoId);
},
onError: () => {
console.error('Ошибка загрузки');
},
});
};
return (
<div>
<input type="file" accept="video/*" onChange={handleFileChange} />
{uploadingProgress > 0 && (
<div>
<progress value={uploadingProgress} max={100} />
<span>{uploadingProgress}%</span>
<button onClick={abortUpload}>Отменить</button>
</div>
)}
</div>
);
};
TUS особенно полезен когда:
Для маленьких файлов (аватарки, документы до 5 МБ) обычный multipart/form-data может быть проще и достаточен.
Протокол TUS — элегантное решение для надёжной загрузки файлов. Библиотека tus-js-client делает интеграцию простой: несколько строк кода, и ваши пользователи больше не будут терять прогресс загрузки при проблемах с сетью.
Полезные ссылки: