كيفية ضغط صورة Base64 (لماذا يفشل Gzip غالباً)
تعرف على تقليل Base64 بتحسين الصورة بعد فكها، ولماذا لا يستبدل Gzip Data URL، وكيف تستهدف 100 KB عبر JavaScript.
لماذا يزيد Base64 نحو 33.3%
يرمز Base64 ثلاثة بايتات بأربعة محارف. الطول الدقيق 4 × ceil(n / 3)، من دون بادئة Data URL.
ثلاث سلاسل للمعالجة
بايتات→Base64 قياسي لكنه أكبر. Base64→Gzip→Base64 يحتاج عكس طبقتين. فك الصورة→تحسينها→Base64 يحفظ صورة عادية لـ img src.
لماذا يتراجع نفع Gzip عند العودة إلى النص
قد يبدو Gzip الوسيط أصغر، لكن Base64 جديداً والترويسات يزيدان الحجم. والناتج Gzip لا JPEG أو PNG أو WebP قابلة للعرض مباشرة.
صيغ الصور مضغوطة أصلاً
تستخدم JPEG وPNG وWebP ضغطها الخاص. يأتي التوفير الحقيقي غالباً من تقليل البكسلات أو الجودة أو البيانات الوصفية أو اختيار صيغة أنسب.
JavaScript صحيح في المتصفح
تحقق من Base64 وmagic bytes، وفك عبر ImageBitmap، وارسم على Canvas محدودة ثم استخدم toBlob. يحتاج الإنتاج حدوداً ورفض الحركة وكشف WebP وحماية من المهام القديمة.
مثال موجز بواجهات المتصفح
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();
}
}
كيفية بلوغ 100 KB
اعتبرها 102,400 بايت للصورة. ابحث جودة JPEG/WebP ضمن حد، ثم صغّر بنسبة sqrt(الهدف/الحالي) في جولات قليلة.
الجودة والأبعاد والصيغة
تغير الجودة codec بينما يحذف تغيير الحجم بكسلات. تناسب الصور JPEG/WebP والشفافية PNG/WebP غالباً. قارن الناتج الفعلي.
شفافية PNG وخلفية JPEG
لا يملك JPEG قناة alpha. احتفظ بـ PNG/WebP أو اطلب خلفية مؤكدة قبل التسطيح.
GIF وWebP المتحرك وSVG
يلتقط Canvas عادة إطاراً واحداً. ارفض GIF وWebP المتحرك وICO؛ يحتاج SVG أداة نصية خاصة.
المحلي أكثر خصوصية وليس إثبات أمان
يمنع الرفع والتخزين الخادمي لكنه ليس فحص malware. تبقى مخاطر الجهاز والإضافات وcodec.
متى نترك Base64
للصور الكبيرة استخدم Blob URL أو ملفاً مستقلاً قابلاً للتخزين المؤقت أو رفعاً ثنائياً عبر Blob/FormData/ArrayBuffer.
الأسئلة الشائعة
هل يمكن ضغط Base64؟
نعم، لكن الضغط العام لا ينتج Base64 صورة قابلاً للاستخدام مباشرة.
هل يعمل في img src؟
فقط إذا كان الناتج بعد الفك صورة؛ يجب فك Gzip أولاً.
كيف أصل إلى 100 KB؟
ابحث الجودة ضمن حد ثم قلل الأبعاد بنسبة ثابتة.
هل تقل الجودة؟
قد يقللها تغيير الحجم والجودة بفقد.
لماذا يزيد 33%؟
تُمثل ثلاثة بايتات بأربعة محارف.
هل تُرفع الصورة؟
يعمل الضاغط داخل المتصفح فقط.
Gzip قبل أم بعد؟
استخدم ضغط HTTP حول المستند لا داخل Data URL.