Cara mengompres gambar Base64 (mengapa Gzip biasanya gagal)
Pelajari cara mengecilkan Base64 dengan mengoptimalkan gambar, mengapa Gzip bukan pengganti Data URL, dan cara menargetkan 100 KB di JavaScript.
Mengapa Base64 sekitar 33,3% lebih besar
Base64 mengenkode tiga byte menjadi empat karakter. Panjang tepatnya 4 × ceil(n / 3), belum termasuk prefiks Data URL.
Tiga alur pemrosesan
Byte→Base64 standar tetapi lebih besar. Base64→Gzip→Base64 memerlukan dua pembalikan. Dekode gambar→optimalkan→Base64 mempertahankan gambar biasa untuk img src.
Mengapa keuntungan Gzip hilang saat kembali ke teks
Gzip perantara mungkin lebih kecil, tetapi Base64 baru dan header menambah ukuran. Hasilnya adalah Gzip, bukan JPEG, PNG, atau WebP yang langsung tampil.
Format gambar sudah terkompresi
JPEG, PNG, dan WebP memakai kompresi sendiri. Penghematan nyata biasanya berasal dari lebih sedikit piksel, kualitas lossy, metadata, atau format yang lebih cocok.
JavaScript browser yang benar
Validasi Base64 dan magic bytes, dekode dengan ImageBitmap, gambar ke Canvas terbatas, lalu gunakan toBlob. Kode produksi perlu batas, penolakan animasi, deteksi WebP, dan perlindungan tugas lama.
Contoh singkat dengan API bawaan
async function compressBase64Image(dataUrl, maxWidth = 1600, quality = 0.82) {
const [header, payload] = dataUrl.split(',', 2);
const mime = header.match(/^data:([^;]+);base64$/)?.[1];
if (!mime || !/^image\/(png|jpeg|webp)$/.test(mime)) throw new Error('Unsupported image');
const binary = atob(payload.replace(/\s+/g, ''));
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
const bitmap = await createImageBitmap(new Blob([bytes], { type: mime }));
try {
const scale = Math.min(1, maxWidth / bitmap.width);
const canvas = document.createElement('canvas');
canvas.width = Math.max(1, Math.round(bitmap.width * scale));
canvas.height = Math.max(1, Math.round(bitmap.height * scale));
const context = canvas.getContext('2d');
if (!context) throw new Error('Canvas 2D unavailable');
context.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
const blob = await new Promise((resolve) => canvas.toBlob(resolve, 'image/webp', quality));
if (!blob) throw new Error('Encoding failed');
const output = new Uint8Array(await blob.arrayBuffer());
let outputBinary = '';
for (let offset = 0; offset < output.length; offset += 0x8000) {
outputBinary += String.fromCharCode(...output.subarray(offset, offset + 0x8000));
}
return `data:${blob.type};base64,${btoa(outputBinary)}`;
} finally {
bitmap.close();
}
}
Cara menuju 100 KB
Gunakan 102.400 byte gambar. Cari kualitas JPEG/WebP dengan batas; lalu skala proporsional memakai sqrt(target/saat ini) hanya beberapa putaran.
Kualitas, dimensi, dan format
Kualitas mengubah codec; resize menghapus piksel. Foto sering cocok JPEG/WebP, transparansi PNG/WebP. Bandingkan output nyata.
Transparansi PNG dan latar JPEG
JPEG tidak punya alpha. Pertahankan PNG/WebP atau minta latar yang dikonfirmasi sebelum flatten.
GIF, WebP animasi, dan SVG
Canvas biasanya menangkap satu frame. Tolak GIF, WebP animasi, dan ICO; SVG perlu pengoptimal teks khusus.
Lokal lebih privat, bukan bukti aman
Tanpa unggah menghindari penyimpanan server, tetapi bukan pemindaian malware. Risiko perangkat, ekstensi, dan codec tetap ada.
Kapan berhenti memakai Base64
Untuk gambar besar gunakan Blob URL, file terpisah yang dapat di-cache, atau unggahan biner Blob/FormData/ArrayBuffer.
Pertanyaan umum
Bisakah Base64 dikompresi?
Bisa, tetapi kompresi umum tidak menghasilkan Base64 gambar yang langsung dipakai.
Bisa untuk img src?
Hanya bila hasil dekode adalah gambar; Gzip harus dibuka dahulu.
Bagaimana menjadi 100 KB?
Cari kualitas dengan batas lalu kurangi dimensi secara proporsional.
Apakah kualitas turun?
Resize dan kualitas lossy dapat menurunkannya.
Mengapa 33% lebih besar?
Tiga byte diwakili empat karakter.
Apakah diunggah?
Kompresor hanya bekerja di browser.
Gzip sebelum atau sesudah?
Gunakan kompresi HTTP di luar dokumen, bukan di dalam Data URL.