Fitur upload gambar merupakan bagian penting dalam pengembangan aplikasi web, seperti sistem manajemen konten, website berita, toko online, dan aplikasi administrasi. Agar penggunaan penyimpanan lebih efisien, gambar dapat diperkecil dimensinya sebelum disimpan.
Pada tutorial ini, kita akan mempelajari cara upload gambar menggunakan PHP, melakukan resize dengan GD Library, lalu menyimpan hasilnya ke database MySQL menggunakan MySQLi.
Ada dua pendekatan untuk menyimpan gambar: menyimpan data gambar secara langsung dalam database atau menyimpan file di folder server dan mencatat path-nya di database. Tutorial ini membahas pendekatan pertama agar kamu memahami proses penyimpanan gambar dalam bentuk binary.
1. Persiapan Database MySQL
Buat database, kemudian buat tabel untuk menyimpan data gambar.
CREATE DATABASE tutorial_upload;
USE tutorial_upload;
CREATE TABLE images (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
image_name VARCHAR(255) NOT NULL,
mime_type VARCHAR(50) NOT NULL,
image_data MEDIUMBLOB NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Kolom image_data menggunakan tipe MEDIUMBLOB, yang dapat menyimpan data binary hingga sekitar 16 MiB per nilai. Batas ini perlu disesuaikan dengan kebutuhan aplikasi dan batas maksimum ukuran file yang diizinkan.
2. Membuat Koneksi ke Database
Buat file bernama db.php untuk menghubungkan aplikasi PHP dengan MySQL.
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$conn = new mysqli(
"localhost",
"username",
"password",
"tutorial_upload"
);
$conn->set_charset("utf8mb4");
Ganti username, password, dan nama database sesuai konfigurasi server kamu.
Kode tersebut menggunakan objek MySQLi dan mengaktifkan pelaporan kesalahan agar masalah database lebih mudah ditangani selama pengembangan. Untuk produksi, pastikan kesalahan internal tidak ditampilkan langsung kepada pengguna.
3. Membuat Form Upload Gambar
Buat file index.php yang berisi form untuk memilih gambar.
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Upload Gambar PHP</title>
</head>
<body>
<h1>Upload dan Resize Gambar</h1>
<form action="upload.php" method="POST" enctype="multipart/form-data">
<label for="image">Pilih gambar:</label>
<input
type="file"
name="image"
id="image"
accept="image/jpeg,image/png,image/webp"
required
>
<button type="submit">Upload Gambar</button>
</form>
</body>
</html>
Atribut enctype="multipart/form-data" wajib digunakan agar file dapat dikirim melalui form. Atribut accept membantu membatasi pilihan file di browser, tetapi bukan pengganti validasi di server.
4. Mengambil dan Memvalidasi File Upload
Selanjutnya, buat file upload.php. Tahap pertama adalah memeriksa apakah file berhasil diterima dan memastikan jenis file yang diizinkan.
<?php
require_once "db.php";
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
http_response_code(405);
exit("Metode permintaan tidak diizinkan.");
}
if (
!isset($_FILES["image"]) ||
!is_array($_FILES["image"])
) {
exit("File gambar tidak ditemukan.");
}
$file = $_FILES["image"];
if ($file["error"] !== UPLOAD_ERR_OK) {
exit("Upload gagal. Silakan coba kembali.");
}
$maxSize = 5 * 1024 * 1024; // 5 MB
if ($file["size"] <= 0 || $file["size"] > $maxSize) {
exit("Ukuran file harus lebih dari 0 dan maksimal 5 MB.");
}
if (!is_uploaded_file($file["tmp_name"])) {
exit("File upload tidak valid.");
}
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->file($file["tmp_name"]);
$allowedTypes = [
"image/jpeg" => "jpg",
"image/png" => "png",
"image/webp" => "webp"
];
if (!isset($allowedTypes[$mimeType])) {
exit("Format gambar tidak didukung.");
}
$originalName = basename($file["name"]);
Penjelasan:
$_FILESdigunakan untuk mengambil informasi file yang dikirim.UPLOAD_ERR_OKmenunjukkan bahwa proses upload tidak melaporkan kesalahan.- Batas 5 MB mencegah file berukuran terlalu besar diproses tanpa kontrol.
finfomembantu memeriksa MIME type berdasarkan isi file, bukan hanya ekstensi namanya.is_uploaded_file()memastikan file berasal dari proses upload HTTP PHP.
Validasi ini merupakan lapisan awal. Untuk aplikasi produksi, kamu juga perlu mengatur batas upload pada konfigurasi PHP dan server.
5. Membaca Gambar dengan GD Library
Setelah validasi selesai, buat objek gambar dari file yang diunggah.
$imageData = file_get_contents($file["tmp_name"]);
$sourceImage = imagecreatefromstring($imageData);
if ($sourceImage === false) {
exit("File tidak dapat dibaca sebagai gambar.");
}
Fungsi file_get_contents() membaca isi file ke dalam string binary. Selanjutnya, imagecreatefromstring() mencoba membuat gambar GD dari data tersebut.
Berbeda dari penggunaan addslashes(), cara ini tidak memerlukan penambahan garis miring pada data gambar. Binary gambar nantinya dikirim ke database melalui prepared statement.
Pastikan ekstensi GD aktif pada instalasi PHP kamu.
6. Menentukan Ukuran Resize Gambar
Misalnya, kita ingin membatasi gambar agar lebar dan tingginya tidak melebihi 500 piksel. Rasio asli tetap dipertahankan sehingga gambar tidak tampak melebar atau memanjang.
$oldWidth = imagesx($sourceImage);
$oldHeight = imagesy($sourceImage);
$maxWidth = 500;
$maxHeight = 500;
$scale = min(
$maxWidth / $oldWidth,
$maxHeight / $oldHeight,
1
);
$newWidth = max(1, (int) round($oldWidth * $scale));
$newHeight = max(1, (int) round($oldHeight * $scale));
Variabel $scale menentukan faktor pengecilan gambar. Nilai maksimum 1 memastikan gambar yang lebih kecil dari batas tidak diperbesar.
Contohnya, gambar berukuran 1.600 ร 1.200 piksel akan menjadi 500 ร 375 piksel. Sementara itu, gambar berukuran 300 ร 200 piksel tetap berukuran 300 ร 200 piksel.
7. Melakukan Resize dengan imagecopyresampled()
Buat gambar baru, lalu salin gambar sumber ke ukuran yang telah ditentukan.
$resizedImage = imagecreatetruecolor($newWidth, $newHeight);
if ($mimeType === "image/png" || $mimeType === "image/webp") {
imagealphablending($resizedImage, false);
imagesavealpha($resizedImage, true);
$transparent = imagecolorallocatealpha(
$resizedImage,
0,
0,
0,
127
);
imagefill($resizedImage, 0, 0, $transparent);
}
imagecopyresampled(
$resizedImage,
$sourceImage,
0,
0,
0,
0,
$newWidth,
$newHeight,
$oldWidth,
$oldHeight
);
Fungsi imagecopyresampled() melakukan penskalaan dengan interpolasi sehingga hasil resize umumnya lebih halus dibandingkan metode penyalinan sederhana.
Kode di atas juga menyiapkan transparansi untuk gambar PNG dan WebP. Namun, dukungan format WebP bergantung pada versi serta konfigurasi GD yang terpasang.
8. Mengubah Gambar Menjadi Data Binary
Setelah proses resize selesai, gambar perlu dikodekan kembali sebelum disimpan ke database. Pada contoh ini, format keluaran dipilih berdasarkan MIME type awal.
ob_start();
try {
$saved = false;
if ($mimeType === "image/jpeg") {
$saved = imagejpeg($resizedImage, null, 85);
} elseif ($mimeType === "image/png") {
$saved = imagepng($resizedImage, null, 6);
} elseif ($mimeType === "image/webp") {
$saved = imagewebp($resizedImage, null, 85);
}
if (!$saved) {
throw new RuntimeException("Gagal mengolah gambar.");
}
$resizedImageBinary = ob_get_contents();
if ($resizedImageBinary === false) {
throw new RuntimeException("Gagal membaca data gambar.");
}
} finally {
ob_end_clean();
imagedestroy($sourceImage);
imagedestroy($resizedImage);
}
Penjelasan:
ob_start()menampung output gambar agar tidak langsung dikirim ke browser.imagejpeg(),imagepng(), danimagewebp()menghasilkan data gambar dalam format masing-masing.- Kualitas JPEG dan WebP diatur ke 85 sebagai titik awal yang dapat disesuaikan.
- Kompresi PNG menggunakan parameter tingkat kompresi, bukan persentase kualitas.
ob_end_clean()membersihkan output buffer setelah proses selesai.imagedestroy()melepaskan sumber daya gambar GD.
Pastikan dukungan format gambar yang digunakan tersedia di server. Kamu juga dapat memilih satu format keluaran yang konsisten, misalnya JPEG untuk foto, jika transparansi tidak diperlukan.
9. Menyimpan Gambar ke Database dengan Prepared Statement
Setelah gambar berhasil di-resize, simpan nama file, MIME type, dan data binary ke tabel images.
$stmt = $conn->prepare(
"INSERT INTO images (image_name, mime_type, image_data)
VALUES (?, ?, ?)"
);
$stmt->bind_param(
"sss",
$originalName,
$mimeType,
$resizedImageBinary
);
$stmt->send_long_data(2, $resizedImageBinary);
$stmt->execute();
echo "Gambar berhasil diupload dan disimpan ke database.";
$stmt->close();
$conn->close();
Prepared statement memisahkan struktur SQL dari data yang dikirim. Ini membantu mencegah SQL injection, sedangkan send_long_data() dapat digunakan untuk mengirim data binary pada parameter yang sesuai.
Indeks parameter 2 pada send_long_data() merujuk pada parameter ketiga, yaitu image_data, karena indeks parameter dimulai dari nol.
Untuk aplikasi produksi, gunakan penanganan exception agar kegagalan penyimpanan tidak menampilkan informasi internal database kepada pengguna.
10. Menampilkan Gambar dari Database
Gambar yang disimpan sebagai binary dapat ditampilkan kembali dengan membaca data dari database dan mengirimkannya sebagai respons HTTP. Untuk implementasi yang lebih rapi, buat file terpisah seperti image.php?id=1.
Contoh dasar:
<?php
require_once "db.php";
$id = filter_input(INPUT_GET, "id", FILTER_VALIDATE_INT);
if (!$id || $id < 1) {
http_response_code(400);
exit("ID gambar tidak valid.");
}
$stmt = $conn->prepare(
"SELECT mime_type, image_data FROM images WHERE id = ?"
);
$stmt->bind_param("i", $id);
$stmt->execute();
$result = $stmt->get_result();
$image = $result->fetch_assoc();
if (!$image) {
http_response_code(404);
exit("Gambar tidak ditemukan.");
}
header("Content-Type: " . $image["mime_type"]);
header("X-Content-Type-Options: nosniff");
echo $image["image_data"];
$stmt->close();
$conn->close();
Dalam aplikasi produksi, pastikan MIME type yang dikirim berasal dari daftar format yang telah diizinkan, bukan data yang dapat dimanipulasi pengguna. Untuk gambar publik, kamu juga dapat menambahkan cache HTTP agar permintaan berulang tidak selalu membebani database.
11. Kelebihan dan Kekurangan Menyimpan Gambar di Database
Kelebihan:
- Data gambar dan informasi terkait tersimpan dalam satu sistem database.
- Backup dan pengelolaan data dapat dilakukan melalui mekanisme database.
- Hak akses gambar dapat diintegrasikan dengan logika aplikasi.
Kekurangan:
- Ukuran database dapat meningkat dengan cepat.
- Backup, pemulihan, dan transfer database dapat menjadi lebih berat.
- Penyajian gambar berulang dapat menambah beban database.
- Binary gambar tidak dapat dioptimalkan melalui cache file statis semudah gambar yang disimpan di folder server.
Untuk website berskala besar, pendekatan yang umum digunakan adalah menyimpan file gambar di folder penyimpanan atau object storage, kemudian menyimpan path, nama file, MIME type, ukuran, dan metadata lainnya di database.
Kesimpulan
Upload dan resize gambar dengan PHP MySQLi dapat dilakukan menggunakan GD Library, mulai dari validasi file, pembacaan gambar, pengaturan dimensi, proses resize, hingga penyimpanan data binary dengan prepared statement.
Kunci implementasi yang baik bukan hanya memperkecil gambar, tetapi juga memvalidasi file, menjaga rasio gambar, mengatur batas ukuran, dan menangani data dengan aman.
Untuk aplikasi sederhana, menyimpan gambar dalam database dapat menjadi pilihan. Namun, untuk website berita, katalog produk, atau aplikasi dengan banyak gambar, menyimpan file di storage dan mencatat path-nya di database sering kali lebih efisien.

