Use Cases

Como implementar carga de archivos con Claude Code

Aprenda a implementar carga de archivos usando Claude Code. Incluye ejemplos practicos de codigo y guia paso a paso.

Usando Claude Code para implementar carga de archivos

La carga de archivos es una funcionalidad con muchos elementos de implementacion: validacion, URLs firmadas, indicador de progreso, redimensionamiento de imagenes, etc. Al delegar todo a Claude Code, puede construir eficientemente una funcionalidad de carga segura y facil de usar.

Carga directa con URLs firmadas

> Implementa carga de archivos usando URLs firmadas de S3.
> Configuracion donde el cliente sube directamente a S3 sin pasar por el servidor.
> Tamano maximo de archivo 10MB, solo imagenes y PDF permitidos.

Lado del servidor (generacion de URL firmada)

// 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();

  // Validacion
  if (!ALLOWED_TYPES.includes(fileType)) {
    return NextResponse.json(
      { error: 'Formato de archivo no permitido' },
      { status: 400 }
    );
  }

  if (fileSize > MAX_SIZE) {
    return NextResponse.json(
      { error: 'El tamano del archivo debe ser menor a 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,
  });
}

Lado del cliente (carga con progreso)

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. Obtener URL firmada
      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. Subir directamente a S3 (con progreso)
      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 };
}

Componente de carga

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">
          Arrastre y suelte archivos, o haga clic para seleccionar
        </p>
        <p className="mt-1 text-sm text-gray-400">
          JPEG, PNG, WebP, PDF (maximo 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">
          Seleccionar archivo
        </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">
          Carga completada
        </p>
      )}
    </div>
  );
}

Procesamiento de redimensionamiento de imagenes

Tambien puede solicitar a Claude Code la implementacion del redimensionamiento automatico de imagenes cargadas con Lambda.

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

Resumen

Con Claude Code, puede implementar eficientemente la funcionalidad de carga de archivos incluyendo URLs firmadas, indicador de progreso, validacion y redimensionamiento de imagenes. Su fortaleza es generar implementaciones seguras de manera integral. Para detalles de integracion con AWS, consulte tambien Automatizacion de despliegue en AWS. Para usos en proyectos personales, consulte Como acelerar el desarrollo personal.

Para mas detalles sobre Claude Code, consulte la documentacion oficial de Anthropic.

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