cloud-storage について
アプリケーション開発者向けの AWS S3 オブジェクトストレージのリファレンススキルです。ファイルのアップロードやダウンロード、バケット設定、運用上の注意点をコード例とともに紹介します。
主な機能
- 01S3のアップロードとダウンロード
- 02署名付きURL
- 03大容量ファイルのマルチパートアップロード
- 04バケットポリシーとCORSの例
- 05トラブルシューティングと本番チェックリスト
活用シーン
- 01クライアントからS3へ直接ファイルをアップロード
- 02有効期限付きダウンロードリンクの生成
- 03ブラウザーアップロードと公開アセットアクセスの設定
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ claude-dev-suite/ claude-dev-suite --skill cloud-storageインストール後、Claude Code で/cloud-storageを入力して読み込まれたか確認します。
詳細
クラウドオブジェクトストレージ
AWS S3(デフォルトとして推奨)
セットアップ(SDK v3)
import { S3Client, PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
const s3 = new S3Client({ region: process.env.AWS_REGION });
アップロード
// Direct upload
await s3.send(new PutObjectCommand({
Bucket: process.env.S3_BUCKET,
Key: `uploads/${userId}/${filename}`,
Body: buffer,
ContentType: mimeType,
Metadata: { 'uploaded-by': userId },
}));
// Presigned URL (client uploads directly to S3)
const url = await getSignedUrl(s3, new PutObjectCommand({
Bucket: process.env.S3_BUCKET,
Key: `uploads/${key}`,
ContentType: mimeType,
}), { expiresIn: 3600 });
ダウンロード用の署名付き URL
const url = await getSignedUrl(s3, new GetObjectCommand({
Bucket: process.env.S3_BUCKET,
Key: fileKey,
}), { expiresIn: 3600 });
マルチパートアップロード(大容量ファイル)
import { CreateMultipartUploadCommand, UploadPartCommand, CompleteMultipartUploadCommand } from '@aws-sdk/client-s3';
import { Upload } from '@aws-sdk/lib-storage';
// Simplified with lib-storage
const upload = new Upload({
client: s3,
params: { Bucket: bucket, Key: key, Body: readableStream },
partSize: 10 * 1024 * 1024, // 10MB parts
leavePartsOnError: false,
});
upload.on('httpUploadProgress', (progress) => console.log(progress));
await upload.done();
Python(boto3)
import boto3
from botocore.config import Config
s3 = boto3.client('s3', config=Config(signature_version='s3v4'))
# Upload
s3.upload_fileobj(file_obj, bucket, key, ExtraArgs={'ContentType': mime_type})
# Presigned URL
url = s3.generate_presigned_url('put_object',
Params={'Bucket': bucket, 'Key': key, 'ContentType': mime_type},
ExpiresIn=3600)
Java(Spring)
@Service
public class S3Service {
private final S3Client s3;
public String generatePresignedUploadUrl(String key, String contentType) {
var presigner = S3Presigner.builder().region(Region.of(region)).build();
var request = PutObjectPresignRequest.builder()
.signatureDuration(Duration.ofHours(1))
.putObjectRequest(b -> b.bucket(bucket).key(key).contentType(contentType))
.build();
return presigner.presignPutObject(request).url().toString();
}
}
バケットポリシーのパターン
アセットの公開読み取り
{
"Statement": [{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/public/*"
}]
}
直接アップロード用の CORS
{
"CORSRules": [{
"AllowedHeaders": ["*"],
"AllowedMethods": ["PUT", "POST"],
"AllowedOrigins": ["https://myapp.com"],
"ExposeHeaders": ["ETag"],
"MaxAgeSeconds": 3600
}]
}
アンチパターン
| アンチパターン | 修正方法 |
|---|---|
| 大容量ファイルをサーバー経由でプロキシする | 直接のクライアントアップロード/ダウンロードには署名付き URL を使用します |
| アップロード時に Content-Type を指定しない | ブラウザーが正しく動作するよう、必ず ContentType を設定します |
| 予測可能なファイルキー | 列挙を防ぐため、UUID またはハッシュ化したパスを使用します |
| ライフサイクルルールがない | 一時アップロードに有効期限を設定し、古いファイルを Glacier に移行します |
| 有効期間の長い署名付き URL | 有効期間を短くします(アップロードは 1 時間、ダウンロードは 15 分) |
| 大容量ファイルにマルチパートを使用しない | 100 MB を超えるファイルにはマルチパートアップロードを使用します |
トラブルシューティングのクイックガイド
| 問題 | 原因 | 修正方法 |
|---|---|---|
| 署名付き URL で 403 が発生する | 期限切れ、リージョンの誤り、または CORS | 有効期限、リージョン、CORS の設定を確認します |
| SignatureDoesNotMatch | ContentType の不一致 | クライアントが署名時と同じ ContentType を送信していることを確認します |
| アップロードが遅い | 大容量ファイルに単一パートを使用している | パートを並列処理するマルチパートアップロードを使用します |
| CORS エラー | CORS 設定がない | バケットに CORSRules を追加します |
| 公開ファイルで AccessDenied が発生する | バケットポリシーがない | プレフィックスに公開読み取りポリシーを追加します |
本番環境チェックリスト
- すべてのクライアントアップロードに署名付き URL を使用する(大容量ファイルをプロキシしない)
- すべてのアップロードで ContentType を設定する
- UUID ベースのキーを使用する(列挙を防ぐ)
- 直接アップロード用に CORS を設定する
- 一時ファイル/古いファイル用のライフサイクルルールを設定する
- 保存データの暗号化を有効にする(SSE-S3 または SSE-KMS)
- アクセスログを有効にする
- 重要なバケットでバージョニングを有効にする
- 公開アセット用に CloudFront CDN を使用する
コメント
Markdown 対応