Use Cases

如何Implement File Upload:Claude Code 实战指南

学习如何implement file upload:Claude Code 实战. 包含实用代码示例和分步指导。

文件上传機能の实现に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: 'us-east-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.us-east-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 #file upload #S3 #React #Node.js