Upload gambar di PHP bisa selesai dalam beberapa baris kode, tetapi versi yang aman butuh pemeriksaan tipe file, pembatasan ukuran, dan penamaan ulang sebelum file disimpan. Alurnya berjalan dari pembuatan form sampai gambar muncul di halaman, lalu membandingkan dua pendekatan penyimpanan yang paling umum dipakai.

Menyiapkan form HTML dengan enctype yang benar

Form upload gagal paling sering karena atribut enctype lupa ditulis. Tanpa multipart/form-data, browser hanya mengirim nama file, bukan isi file, sehingga $_FILES kosong di sisi server.

<form action="upload.php" method="post" enctype="multipart/form-data">
    <input type="file" name="gambar" accept="image/png,image/jpeg,image/webp">
    <button type="submit">Unggah</button>
</form>

Atribut accept membantu pemilih file di browser menyaring ekstensi, tetapi tidak mencegah siapa pun mengirim file lain lewat request manual. Semua pemeriksaan tetap dilakukan di PHP.

Memeriksa error dan ukuran file sebelum diproses

Ketika form dikirim, PHP mengisi $_FILES['gambar'] dengan lima kunci: name, type, tmp_name, error, dan size. Nilai error harus nol dulu sebelum pemeriksaan lain dijalankan.

  • UPLOAD_ERR_OK (0) berarti file terkirim utuh.
  • UPLOAD_ERR_INI_SIZE muncul saat file melebihi upload_max_filesize di php.ini.
  • UPLOAD_ERR_NO_FILE terjadi bila pengguna mengirim form tanpa memilih file.

Batas ukuran sebaiknya dicek dua kali: sekali di PHP dengan angka eksplisit, sekali lagi lewat konfigurasi server. Nilai size dari $_FILES adalah ukuran file sementara dalam byte, jadi perbandingannya memakai satuan yang sama.

Cara upload gambar di PHP dengan validasi tipe file

Jangan percaya ekstensi atau $_FILES['type'], karena keduanya berasal dari klien. Fungsi getimagesize() membaca header file dan mengembalikan dimensi serta tipe gambar, sehingga file teks yang diberi nama .jpg akan ditolak.

$info = getimagesize($_FILES['gambar']['tmp_name']);
if ($info === false) {
    exit('File bukan gambar yang valid.');
}

$diizinkan = [IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_WEBP];
if (!in_array($info[2], $diizinkan, true)) {
    exit('Format gambar tidak didukung.');
}

$maksimal = 2 * 1024 * 1024; // 2 MB
if ($_FILES['gambar']['size'] > $maksimal) {
    exit('Ukuran gambar terlalu besar.');
}

Fungsi ini juga memberi dimensi piksel. Kamu bisa memakainya untuk menolak gambar yang terlalu kecil atau terlalu besar sebelum proses resize. Untuk gambar yang dimensinya perlu diubah, ekstensi GD atau Imagick tersedia di sebagian besar hosting PHP.

Menentukan nama file dan lokasi penyimpanan

Nama file dari pengguna tidak boleh dipakai langsung. Karakter seperti spasi, garis miring, dan titik ganda bisa membingungkan sistem file atau membuka celah path traversal. Ganti nama dengan kombinasi acak dan ekstensi yang ditentukan sendiri berdasarkan tipe gambar yang lolos validasi.

$ekstensi = match($info[2]) {
    IMAGETYPE_JPEG => 'jpg',
    IMAGETYPE_PNG  => 'png',
    IMAGETYPE_WEBP => 'webp',
};

$namaBaru = bin2hex(random_bytes(16)) . '.' . $ekstensi;
$tujuan = __DIR__ . '/uploads/' . $namaBaru;

if (!move_uploaded_file($_FILES['gambar']['tmp_name'], $tujuan)) {
    exit('Gagal menyimpan file.');
}

move_uploaded_file() memastikan file yang dipindahkan benar-benar hasil upload, bukan file lain di server. Folder uploads harus ada dan bisa ditulis oleh proses PHP, tetapi izin tulis tidak perlu diberikan ke seluruh pengguna sistem.

Simpan nama file hasil upload di kolom database, bukan path lengkap. Path bisa berubah ketika folder dipindah atau domain berganti, sedangkan nama file tetap sama.

Dua pendekatan penyimpanan: folder publik vs luar direktori web

Menyimpan gambar langsung di public/uploads membuat penyajian sederhana karena browser bisa memuatnya tanpa skrip perantara. Risikonya, semua file di folder itu dapat diakses publik, termasuk file yang seharusnya belum dipublikasikan.

Aspek Folder publik Di luar web root
Kecepatan saji Langsung dari server web Lewat skrip PHP
Kontrol akses Terbatas pada aturan server Bisa dicek per pengguna
Kompleksitas Rendah Butuh endpoint pembaca file
Cocok untuk Gambar artikel, avatar Dokumen privat, bukti transfer

Pilihan pertama cocok untuk situs dengan gambar yang memang publik. Pilihan kedua dipakai ketika file hanya boleh dilihat pemiliknya, misalnya lampiran tiket dukungan. Endpoint pembaca file tetap harus memeriksa sesi pengguna dan mengirim header Content-Type yang sesuai.

Menampilkan gambar hasil upload di halaman

Setelah nama file tersimpan di database, tampilkan gambar dengan elemen img yang sumbernya dibangun dari nama file tersebut. Fungsi htmlspecialchars() tetap dipakai untuk mencegah karakter tak terduga masuk ke atribut HTML.

<img src="uploads/<?= htmlspecialchars($namaFile) ?>"
     alt="Gambar unggahan pengguna"
     loading="lazy">

Untuk daftar gambar yang panjang, pertimbangkan memuat foto secara bertahap. Teknik menampilkan data MySQL ke HTML dengan perulangan dan potongan halaman sudah dibahas di panduan menampilkan data MySQL ke HTML, dan pola yang sama berlaku untuk galeri unggahan.

Kesalahan yang paling sering terjadi

Beberapa masalah muncul berulang di forum dan grup diskusi. Memeriksa daftar ini lebih dulu bisa menghemat waktu debugging.

  • Lupa enctype sehingga $_FILES kosong.
  • Memakai $_FILES['gambar']['name'] sebagai nama tujuan sehingga file bertabrakan atau berisi karakter aneh.
  • Tidak mengecek error, lalu memproses tmp_name yang sebenarnya tidak ada.
  • Memberi izin 777 ke folder unggahan padahal cukup izin tulis untuk pengguna server web.
  • Membiarkan file .php tersimpan di folder unggahan, yang bisa dieksekusi bila konfigurasi server salah.

Poin terakhir layak ditekankan: blokir eksekusi skrip di folder unggahan lewat konfigurasi server, dan tetap batasi tipe file di PHP. Dua lapisan pemeriksaan lebih aman daripada satu.

Jika kamu baru mulai memakai PHP, latihan upload file adalah cara bagus memahami cara kerja request dan superglobal. Materi dasar lain ada di tips praktis belajar PHP untuk pemula. Untuk antarmuka form yang lebih interaktif, contoh cara manipulasi DOM dengan jQuery untuk pemula bisa dipakai menampilkan pratinjau gambar sebelum diunggah.

Untuk menampilkan daftar gambar dalam jumlah banyak, halaman yang memotong hasil per sejumlah baris akan lebih ringan. Contoh penerapannya ada di tutorial cara membuat pagination PHP tanpa library tambahan.

Ringkasan

Alur upload gambar di PHP terdiri dari form dengan enctype benar, pemeriksaan error dan ukuran, validasi tipe lewat getimagesize(), penamaan ulang dengan random_bytes(), pemindahan memakai move_uploaded_file(), lalu penyimpanan nama file ke database. Pilihan lokasi penyimpanan menentukan siapa yang bisa mengakses gambar, jadi sesuaikan dengan sifat datanya.

Pertanyaan yang Sering Diajukan

Apakah validasi ekstensi file sudah cukup untuk keamanan?

Belum. Ekstensi mudah dipalsukan, jadi pemeriksaan harus membaca isi file, misalnya dengan getimagesize() atau fungsi MIME terkait. Batasi juga eksekusi skrip di folder tempat file disimpan.

Kenapa file gagal dipindahkan meski validasi lolos?

Periksa izin tulis folder tujuan dan pastikan folder tersebut ada. Nilai upload_max_filesize serta post_max_size di php.ini juga bisa memotong request sebelum skrip berjalan, sehingga $_FILES tidak terisi seperti yang diharapkan.

Bagaimana menangani banyak gambar dalam satu form?

Gunakan atribut name="gambar[]" dengan multiple di input file. Struktur $_FILES akan berubah menjadi array per kunci, jadi perulangan perlu disusun ulang sebelum validasi dan pemindahan dilakukan untuk tiap file.