Claude Codeでファイルアップロード機能を実装する方法
Claude Codeを使ってファイルアップロード機能をS3連携、バリデーション、プログレス表示まで含めて効率的に実装する方法を解説します。
ファイルアップロード機能の実装にClaude Codeを使う
ファイルアップロードはバリデーション、署名付きURL、プログレス表示、画像リサイズなど実装項目が多い機能です。Claude Codeに一括して依頼することで、セキュアで使いやすいアップロード機能を効率的に構築できます。
署名付きURLによるダイレクトアップロード
> S3の署名付きURLを使ったファイルアップロードを実装して。
> サーバーを経由せず、クライアントから直接S3にアップロードする構成で。
> ファイルサイズは10MB以下、画像とPDFのみ許可して。
サーバーサイド(署名付きURL発行)
// src/app/api/upload/route.ts
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { NextRequest, NextResponse } from 'next/server';
import { nanoid } from 'nanoid';
const s3 = new S3Client({ region: 'ap-northeast-1' });
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp', 'application/pdf'];
const MAX_SIZE = 10 * 1024 * 1024; // 10MB
export async function POST(req: NextRequest) {
const { fileName, fileType, fileSize } = await req.json();
// バリデーション
if (!ALLOWED_TYPES.includes(fileType)) {
return NextResponse.json(
{ error: '許可されていないファイル形式です' },
{ status: 400 }
);
}
if (fileSize > MAX_SIZE) {
return NextResponse.json(
{ error: 'ファイルサイズは10MB以下にしてください' },
{ status: 400 }
);
}
const ext = fileName.split('.').pop();
const key = `uploads/${nanoid()}.${ext}`;
const command = new PutObjectCommand({
Bucket: process.env.S3_BUCKET!,
Key: key,
ContentType: fileType,
ContentLength: fileSize,
});
const signedUrl = await getSignedUrl(s3, command, { expiresIn: 300 });
return NextResponse.json({
uploadUrl: signedUrl,
fileUrl: `https://${process.env.S3_BUCKET}.s3.ap-northeast-1.amazonaws.com/${key}`,
key,
});
}
クライアントサイド(プログレス付きアップロード)
import { useState, useCallback } from 'react';
interface UploadState {
progress: number;
uploading: boolean;
error: string | null;
fileUrl: string | null;
}
export function useFileUpload() {
const [state, setState] = useState<UploadState>({
progress: 0,
uploading: false,
error: null,
fileUrl: null,
});
const upload = useCallback(async (file: File) => {
setState({ progress: 0, uploading: true, error: null, fileUrl: null });
try {
// 1. 署名付きURLを取得
const res = await fetch('/api/upload', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
fileName: file.name,
fileType: file.type,
fileSize: file.size,
}),
});
if (!res.ok) {
const { error } = await res.json();
throw new Error(error);
}
const { uploadUrl, fileUrl } = await res.json();
// 2. S3に直接アップロード(プログレス付き)
await new Promise<void>((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', (e) => {
if (e.lengthComputable) {
setState(prev => ({ ...prev, progress: Math.round((e.loaded / e.total) * 100) }));
}
});
xhr.addEventListener('load', () => {
if (xhr.status >= 200 && xhr.status < 300) resolve();
else reject(new Error('Upload failed'));
});
xhr.addEventListener('error', () => reject(new Error('Network error')));
xhr.open('PUT', uploadUrl);
xhr.setRequestHeader('Content-Type', file.type);
xhr.send(file);
});
setState({ progress: 100, uploading: false, error: null, fileUrl });
return fileUrl;
} catch (err) {
setState(prev => ({
...prev,
uploading: false,
error: (err as Error).message,
}));
throw err;
}
}, []);
return { ...state, upload };
}
アップロードコンポーネント
function FileUploader() {
const { progress, uploading, error, fileUrl, upload } = useFileUpload();
const [dragOver, setDragOver] = useState(false);
const handleDrop = useCallback(async (e: React.DragEvent) => {
e.preventDefault();
setDragOver(false);
const file = e.dataTransfer.files[0];
if (file) await upload(file);
}, [upload]);
const handleFileSelect = useCallback(async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) await upload(file);
}, [upload]);
return (
<div>
<div
onDragOver={(e) => { e.preventDefault(); setDragOver(true); }}
onDragLeave={() => setDragOver(false)}
onDrop={handleDrop}
className={`border-2 border-dashed rounded-lg p-8 text-center transition-colors
${dragOver ? 'border-blue-500 bg-blue-50' : 'border-gray-300'}
${uploading ? 'pointer-events-none opacity-50' : 'cursor-pointer'}`}
>
<p className="text-gray-600">
ファイルをドラッグ&ドロップ、またはクリックして選択
</p>
<p className="mt-1 text-sm text-gray-400">
JPEG, PNG, WebP, PDF(10MB以下)
</p>
<input
type="file"
accept="image/jpeg,image/png,image/webp,application/pdf"
onChange={handleFileSelect}
className="hidden"
id="file-input"
/>
<label htmlFor="file-input" className="mt-4 inline-block cursor-pointer rounded bg-blue-600 px-4 py-2 text-white">
ファイルを選択
</label>
</div>
{uploading && (
<div className="mt-4">
<div className="h-2 rounded-full bg-gray-200">
<div
className="h-full rounded-full bg-blue-600 transition-all"
style={{ width: `${progress}%` }}
/>
</div>
<p className="mt-1 text-sm text-gray-500">{progress}%</p>
</div>
)}
{error && <p className="mt-2 text-sm text-red-600">{error}</p>}
{fileUrl && (
<p className="mt-2 text-sm text-green-600">
アップロード完了
</p>
)}
</div>
);
}
画像リサイズの処理
アップロードされた画像をLambdaで自動リサイズする処理もClaude Codeに依頼できます。
// lambda/resize-image.ts
import { S3Event } from 'aws-lambda';
import { S3Client, GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';
import sharp from 'sharp';
const s3 = new S3Client({});
const SIZES = [
{ suffix: 'thumb', width: 200, height: 200 },
{ suffix: 'medium', width: 800 },
{ suffix: 'large', width: 1600 },
];
export async function handler(event: S3Event) {
for (const record of event.Records) {
const bucket = record.s3.bucket.name;
const key = decodeURIComponent(record.s3.object.key);
const { Body } = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
const buffer = Buffer.from(await Body!.transformToByteArray());
for (const size of SIZES) {
const resized = await sharp(buffer)
.resize(size.width, size.height, { fit: 'cover' })
.webp({ quality: 80 })
.toBuffer();
const resizedKey = key.replace(/\.[^.]+$/, `-${size.suffix}.webp`);
await s3.send(new PutObjectCommand({
Bucket: bucket,
Key: resizedKey,
Body: resized,
ContentType: 'image/webp',
}));
}
}
}
まとめ
Claude Codeを使えば、ファイルアップロード機能を署名付きURL、プログレス表示、バリデーション、画像リサイズまで含めて効率的に実装できます。セキュリティを考慮した実装を一括で生成できるのが強みです。AWS連携の詳細はAWSデプロイの自動化も参照してください。個人プロジェクトでの活用法は個人開発を爆速にする方法で紹介しています。
Claude Codeの詳細はAnthropic公式ドキュメントをご覧ください。
#Claude Code
#ファイルアップロード
#S3
#React
#Node.js
関連記事
Use Cases
Use Cases
Claude CodeでCORS設定完全ガイド:クロスオリジン通信の実践解説
Claude Codeを活用したCORS設定の完全ガイド。プリフライトリクエスト、認証付きリクエスト、トラブルシューティングまで実践的に解説します。
Use Cases
Use Cases
Claude Codeで通貨フォーマットを正しく実装する
Claude Codeを使って、多通貨対応・ロケール別フォーマット・為替レート変換など、通貨の表示と処理を正しく実装する方法を解説します。
Use Cases
Use Cases
Claude CodeでDiscord Botを開発する実践ガイド
Claude Codeを活用したDiscord Botの開発方法を解説。discord.js、スラッシュコマンド、ボタン操作、Embed、音声チャンネル連携まで実践的に紹介します。