Upload File dengan fetch dan FormData
Praktik upload file: FormData, progress, validasi ukuran/tipe, dan kenapa Content-Type jangan diset manual.
Mengirim paket berisi barang pecah belah
Mengirim JSON seperti mengirim surat: satu amplop, isi teks. Upload file seperti mengirim paket berisi barang pecah belah: butuh kemasan khusus (multipart), label tiap barang (nama field dan nama file), dan penerima harus tahu cara membongkarnya. Di JavaScript, kemasan itu bernama FormData.
Kenapa tidak pakai JSON untuk file
JSON hanya teks; file biner (gambar, PDF) harus di-encode dulu (misalnya base64) yang membengkakkan ukuran 33%. Multipart/form-data mengirim biner apa adanya, jauh lebih efisien. Itulah standar web untuk upload file sejak era form HTML klasik.
Contoh 1: upload satu file
const input = document.querySelector("#foto");
const file = input.files[0];
const form = new FormData();
form.append("foto", file, file.name);
const response = await fetch("/api/upload", {
method: "POST",
body: form
});
const hasil = await response.json();
console.log(hasil.url);Perhatikan: TIDAK ada header Content-Type. Browser mengisinya otomatis menjadi multipart/form-data; boundary=... dengan boundary yang benar. Menyetel manual adalah error klasik yang merusak format.
Contoh 2: validasi sebelum upload
const MAKS = 2 * 1024 * 1024; // 2 MB
const BOLEH = ["image/jpeg", "image/png"];
function validasi(file) {
if (!BOLEH.includes(file.type)) return "Hanya JPG/PNG";
if (file.size > MAKS) return "Maksimal 2 MB";
return null;
}
const err = validasi(file);
if (err) {
tampilkanError(err);
} else {
upload(file);
}Validasi di frontend untuk UX (langsung ditolak tanpa menunggu upload), tapi server WAJIB validasi ulang: tipe file dari header bisa dipalsukan, jadi periksa magic bytes isi file di backend.
Kesalahan umum
Salah: set Content-Type manual untuk FormData. Boundary salah, server gagal parse. Yang benar: jangan sentuh, biarkan browser.
Salah: tidak ada batas ukuran. User mengupload video 2 GB dan server kehabisan memori. Yang benar: batasi di frontend DAN backend.
Salah: percaya ekstensi file. virus.exe diganti jadi foto.jpg tetap berbahaya. Yang benar: validasi MIME + isi file di server, simpan di luar web root.
Salah: tidak ada umpan balik progress. Upload besar tanpa progress bar membuat user mengira aplikasi hang. Catatan: fetch() tidak mendukung progress upload; untuk itu pakai XMLHttpRequest dengan event upload.onprogress.
Preview gambar sebelum upload
UX yang baik: tampilkan preview dulu agar user yakin file-nya benar:
input.addEventListener("change", function() {
const file = input.files[0];
const err = validasi(file);
if (err) return tampilkanError(err);
const url = URL.createObjectURL(file);
document.querySelector("#preview").src = url;
});URL.createObjectURL membuat URL sementara untuk file lokal tanpa mengupload apa pun. User melihat pratinjau, baru menekan tombol upload. Kombinasikan dengan validasi ukuran/tipe dari modul ini untuk pengalaman upload yang profesional.
Catatan teknis: Untuk banyak file, loop
form.append("foto[]", file)atau beberapa input. Di sisi server (Express), pakai middleware seperti multer; di Laravel,$request->file("foto")dengan validasimimes:jpg,png|max:2048.
export default function App(): JSX.Element { return <h1>Hello world</h1> }
Tantangan
Upload file ke API latihan
Latihan upload tanpa backend sendiri:
- Buat FormData, append sebuah File dari input (boleh file teks kecil).
- POST ke https://httpbin.org/post (API echo gratis) TANPA set Content-Type.
- Baca response JSON: cari bagian "files" dan pastikan nama file-mu muncul.
Tulis kode + apa yang kamu temukan di response.