Upload de arquivos e imagens
Um <input type="file"> sozinho não diz se o usuário selecionou o arquivo certo — preview e feedback fazem parte do upload.
Um <input type="file"> sozinho não dá feedback nenhum sobre o que foi selecionado — o usuário clica, escolhe um arquivo, e não tem confirmação visual de que pegou o certo. Um fluxo de upload de verdade mostra uma prévia antes de enviar e comunica erro quando o arquivo não serve.
Input file — capturando o arquivo selecionado
"use client";
function InputComprovante({ onSelecionar }: { onSelecionar: (file: File) => void }) {
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
const file = e.target.files?.[0];
if (file) onSelecionar(file);
}
return <input type="file" accept="image/*" onChange={handleChange} />;
}Preview antes de enviar
Pra imagem, URL.createObjectURL(file) gera uma URL temporária, local, que aponta direto pro arquivo já selecionado no navegador — sem precisar enviar nada pro servidor só pra mostrar a prévia. O cuidado: essa URL precisa ser liberada com URL.revokeObjectURL quando não for mais usada, senão ela fica ocupando memória.
"use client";
import { useEffect, useState } from "react";
export function PreviewComprovante({ file }: { file: File | null }) {
const [preview, setPreview] = useState<string | null>(null);
useEffect(() => {
if (!file) return;
const url = URL.createObjectURL(file);
setPreview(url);
return () => URL.revokeObjectURL(url); // libera a memória ao trocar/desmontar
}, [file]);
if (!preview) return null;
return <img src={preview} alt="Prévia do comprovante" className="w-40 h-40 object-cover rounded-lg" />;
}Enviando com FormData
Arquivos não vão dentro de um JSON.stringify — eles vão num FormData. E não é preciso definir o header Content-Type manualmente: o navegador já define multipart/form-data com o boundary correto sozinho quando o corpo é um FormData.
async function enviarComprovante(file: File, pedidoId: string) {
const formData = new FormData();
formData.append("arquivo", file);
formData.append("pedidoId", pedidoId);
await fetch("/api/comprovantes", {
method: "POST",
body: formData, // sem Content-Type manual — o navegador cuida disso
});
}Exercício
Adicione validação em InputComprovante: rejeite arquivos maiores que 5MB ou que não sejam do tipo image/*, mostrando uma mensagem de erro em vez de aceitar o arquivo.