Use Cases

Cara Implement File Upload dengan Claude Code

Pelajari cara implement file upload menggunakan Claude Code. Dilengkapi contoh kode praktis dan panduan langkah demi langkah.

implementasiにClaude Codeを使う fileupload機能

fileupload validasi、署名付きURL、プログレスtampilan、gambarリサイズ dll.implementasi項目 多い機能.Claude Code 一括 依頼 こ dan 、セキュア 使いやすいupload機能 efisien pembangunan bisa dilakukan.

署名付きURLによるダイレクトupload

> S3 署名付きURL 使ったfileupload implementasikan.
> server 経由せず、client dari 直接S3 uploadする構成 dengan 。
> fileサイズ 10MB以下、gambar dan PDF み許可して。

serverサイド(署名付き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();

  // validasi
  if (!ALLOWED_TYPES.includes(fileType)) {
    return NextResponse.json(
      { error: '許可されてい tidakfile形式 す' },
      { status: 400 }
    );
  }

  if (fileSize > MAX_SIZE) {
    return NextResponse.json(
      { error: 'fileサイズ 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,
  });
}

clientサイド(プログレス付きupload)

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 pengambilan
      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 直接upload(プログレス付き)
      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 };
}

uploadkomponen

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>
  );
}

pemrosesan gambarリサイズ

uploadされたgambar Lambda 自動リサイズ pemrosesan juga Claude Code 依頼 bisa dilakukan.

// 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',
      }));
    }
  }
}

Summary

Dengan Claude Code, fileupload機能 署名付きURL、プログレスtampilan、validasi、gambarリサイズま 含めてefisien implementasi bisa dilakukan.セキュリティ 考慮 implementasi 一括 generate きる 強み.AWSintegrasi 詳細 AWSdeploy otomatisasi juga bisa dijadikan referensi.個人proyek pemanfaatan法 個人pengembangan 爆速 metode perkenalan い.

Untuk detail Claude Code, lihat Dokumentasi Resmi Anthropic.

#Claude Code #file upload #S3 #React #Node.js