S3 Presigned URLでワンタイムダウンロードリンクを作る
問題
ファイルをダウンロードできるリンクを提供したいが、誰でも無制限にダウンロードできてはいけません。時間制限とダウンロード回数制限が必要です。
解決方法
import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
const s3 = new S3Client({ region: 'ap-northeast-2' });
async function generateDownloadUrl(bucket: string, key: string) {
const command = new GetObjectCommand({ Bucket: bucket, Key: key });
const url = await getSignedUrl(s3, command, {
expiresIn: 300, // 5分
});
return url;
}
// DynamoDBにトークンを保存(1時間TTL、最大1回ダウンロード)
const token = crypto.randomUUID();
await docClient.send(new PutCommand({
TableName: 'download-tokens',
Item: {
PK: `TOKEN#${token}`,
downloadCount: 0,
maxDownloads: 1,
ttl: Math.floor(Date.now() / 1000) + 3600,
},
}));
ポイント
- Presigned URLは署名情報がURLに含まれているため、AWS資格情報なしで直接ダウンロードできます。有効期限が過ぎると403が返されます。
- DynamoDB TTLを設定すると、期限切れのトークンが自動削除されます。クーロンジョブでのクリーンアップは不要です。
expiresInはURL自体の有効時間、DynamoDB TTLはトークンの有効時間です。これらを分離して管理するとより柔軟です。