tusプロトコル
tusは再開可能なファイルアップロード用のオープンプロトコルです。ネットワークの中断に耐えられる信頼性の高いアップロードに使用します。
なぜtusか?
- 再開可能:ネットワーク障害後もアップロードを継続できる
- 効率的:最初からではなく、中断したところから再開
- 標準化:多くのサーバー実装がある明確に定義されたプロトコル
- チャンク化:大きなファイルを自動的に処理
インストール
npm install @samithahansaka/dropup tus-js-client
tus-js-clientはピア依存関係です。
基本的な使い方
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('アップロード完了:', file.uploadedUrl);
},
});
return (
<div>
<div {...getDropProps()} style={styles.dropzone}>
<input {...getInputProps()} />
<p>再開可能なアップロード用にファイルをドロップ</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()}>
アップロード
</button>
</div>
);
}
設定オプション
createTusUploader({
// 必須
endpoint: 'https://your-tus-server.com/files/',
// チャンクサイズ(デフォルト: Infinity = チャンクなし)
chunkSize: 5 * 1024 * 1024, // 5MBチャンク
// リトライ設定
retryDelays: [0, 1000, 3000, 5000], // 0秒、1秒、3秒、5秒後にリトライ
// すべてのリクエストのヘッダー
headers: {
'Authorization': 'Bearer token',
},
// サーバーに送信するメタデータ
metadata: {
filename: 'file.name',
filetype: 'file.type',
// カスタムメタデータを追加
userId: 'user123',
},
// localStorageを介した再開機能を有効化
storeFingerprintForResuming: true,
// 成功したアップロード後にフィンガープリントを削除
removeFingerprintOnSuccess: true,
// 並列アップロード
parallelUploads: 3,
});
以前のアップロードを再開
tusは中断されたアップロードを再開できます:
const { files, actions } = useDropup({
upload: createTusUploader({
endpoint: 'https://your-tus-server.com/files/',
// 再開用にアップロードURLを保存
storeFingerprintForResuming: true,
// オプション: カスタムストレージキー
urlStorageKey: 'my-app-tus-uploads',
onShouldRetry: (error, retryAttempt) => {
// カスタムリトライロジック
if (error.message.includes('network')) {
return retryAttempt < 5;
}
return false;
},
}),
});
// 再追加時にファイルは自動的に再開できます
// または手動でリトライ:
actions.retry(['file-id']);
進捗追跡
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(`アップロード開始: ${file.name}`);
},
onUploadComplete: (file) => {
console.log(`完了: ${file.uploadedUrl}`);
},
});
return (
<div>
<div {...getDropProps()}>
<input {...getInputProps()} />
<p>ここにファイルをドロップ</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>
);
}
一時停止と再開
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>ここにファイルをドロップ</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)}>
一時停止
</button>
)}
{file.status === 'paused' && (
<button onClick={() => actions.retry([file.id])}>
再開
</button>
)}
{file.status === 'error' && (
<button onClick={() => actions.retry([file.id])}>
リトライ
</button>
)}
</div>
))}
<button onClick={() => actions.upload()}>
すべて開始
</button>
</div>
);
}
サーバーセットアップ
tusd(公式サーバー)
# tusdをインストール
go install github.com/tus/tusd/cmd/tusd@latest
# デフォルトオプションで実行
tusd -upload-dir=/uploads
# 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' }),
});
// Expressで
app.all('/files/*', server.handle.bind(server));
カスタムメタデータ処理
// サーバーサイド: メタデータにアクセス
const server = new Server({
path: '/files',
datastore: new FileStore({ directory: './uploads' }),
onUploadCreate: async (req, res, upload) => {
// クライアントから送信されたメタデータにアクセス
console.log(upload.metadata);
// { filename: 'photo.jpg', filetype: 'image/jpeg', userId: 'user123' }
},
onUploadFinish: async (req, res, upload) => {
// 完了したアップロードを処理
console.log('アップロード完了:', upload.id);
},
});
tus拡張機能
Dropupのtus統合は以下のtus拡張機能をサポートしています:
| 拡張機能 | 説明 |
|---|---|
| Creation | 新しいアップロードを作成 |
| Termination | アップロードをキャンセル |
| Checksum | 整合性を検証 |
| Concatenation | 並列アップロード |
| Expiration | 自動クリーンアップ |
エラー処理
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('ネットワークオフライン、接続時に再開します');
} else if (error.code === 'TUS_EXPIRED') {
console.log('アップロード期限切れ、新規に開始します');
actions.retry([file.id]);
} else {
console.error('アップロード失敗:', error.message);
}
},
});
大きなファイルのアップロード
tusは大きなファイルに最適です:
function LargeFileUploader() {
const { files, actions, getDropProps, getInputProps } = useDropup({
maxSize: 10 * 1024 * 1024 * 1024, // 10GB制限
upload: createTusUploader({
endpoint: 'https://your-tus-server.com/files/',
chunkSize: 50 * 1024 * 1024, // 50MBチャンク
retryDelays: [0, 3000, 5000, 10000, 20000],
storeFingerprintForResuming: true,
}),
onUploadProgress: (file, progress) => {
// 大きなファイルの詳細な進捗を表示
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>大きなファイルをアップロード(最大10GB)</p>
</div>
);
}
比較:tus vs チャンクアップロード
| 機能 | tus | カスタムチャンク |
|---|---|---|
| 再開機能 | ビルトイン | 手動 |
| サーバーサポート | 多くのオプション | カスタム |
| プロトコルオーバーヘッド | 高め | 低め |
| 設定 | 標準化 | 柔軟 |
| 最適な用途 | 大きなファイル、不安定なネットワーク | シンプルなアップロード |