مولّد Data URI
رمّز صورة أو خطًا أو نصًا بصيغة data: URI، أو فُك ترميز واحد، مع قياس Base64 مقابل ترميز النسبة المئوية. يعمل داخل متصفحك.
data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='%232563eb'%20stroke-width='2'%3E%3Ccircle%20cx='12'%20cy='12'%20r='9'/%3E%3Cpath%20d='M8%2012.5l2.5%202.5%205-6'/%3E%3C/svg%3E
ترميز النسبة المئوية أقصر هنا: 255 محرفًا مقابل 272، بتوفير 17.
الأحجام
- بايتات المصدر
- 174
- العنوان بترميز Base64
- 272
- العنوان بترميز النسبة المئوية
- 255
ملف داخل عنوان
الـ data URI ملف كامل مكتوب بأكمله على هيئة عنوان. فبدل أن يشير إلى مورد يذهب المتصفح لجلبه، يحمل البايتات نفسها، فتستطيع أيقونة أن تسكن داخل ورقة الأنماط التي تستعملها، وصورة صغيرة أن تسكن داخل صفحة HTML التي تعرضها. عرّف RFC 2397 هذه الخطة عام 1998 وهي مدعومة في كل مكان منذ عقدين.
القواعد قصيرة. بعد اسم الخطة يأتي نوع وسائط اختياري مع معاملات اختيارية، ثم راية base64 اختيارية، ثم فاصلة، ثم البيانات. كل ما قبل الفاصلة ترويسة وكل ما بعدها حمولة — ولهذا يقسم المتصفح عند أول فاصلة، ولهذا لا تضر فاصلة داخل الحمولة.
data:[<mediatype>][;base64],<data> data:,hello text/plain;charset=US-ASCII data:text/plain;charset=utf-8,hello the same bytes, spelled out data:image/png;base64,iVBORw0KGgo= binary, Base64 encoded data:image/svg+xml,%3Csvg%20... markup, percent-encoded
الترويسة الفارغة صالحة وتعني text/plain;charset=US-ASCII، وهي الافتراض الوحيد الذي تمنحك إياه المواصفة. وتبلّغ هذه الصفحة أنها طبّقته بدل أن تعرض نوع وسائط لم تكتبه أنت.
ترميزان، وأيهما أقصر
تعرّف المواصفة طريقتين لكتابة الحمولة، والاختيار بينهما ليس مسألة ذوق — فلملف بعينه تكون إحداهما أقصر بفارق معتبر، وأيّهما تعتمد كليًا على ما يحتويه الملف.
يعيد Base64 كتابة كل ثلاث بايتات على هيئة أربعة محارف من أبجدية من 64 محرفًا. الكلفة ثابتة ومتوقعة: ثلث أكثر بالضبط، زائد الحشو. وهو يصلح لأي شيء، ولهذا يلجأ إليه الناس افتراضيًا.
أما ترميز النسبة المئوية فيترك أي محرف صالح في العنوان كما هو تمامًا، وينفق ثلاثة محارف على كل محرف ليس كذلك. فمع حمولة معظمها ASCII بسيط — ملف SVG، ورقة أنماط صغيرة، كتلة JSON — تمرّ أغلب البايتات بلا مساس وتخرج النتيجة أقصر من Base64، وغالبًا بنسبة عشرين إلى ثلاثين في المئة. أما مع صورة PNG أو خط، حيث يحتاج كل بايت تقريبًا إلى هروب، فتقارب ثلاثة أضعاف الأصل وتصير أسوأ بكثير من Base64.
ومقدار ما يُهرَّب قرار بحد ذاته، وهذه الصفحة تعرض الإجابتين:
- المجموعة الصارمة تُهرّب كل بايت خارج المحارف غير المحجوزة في RFC 3986 — الحروف والأرقام والشرطة والنقطة والشرطة السفلية والمدّة. النتيجة آمنة في أي سياق كان، مقابل تهريب علامات ترقيم لم تكن بحاجة إليه.
- المجموعة الدنيا تُبقي كل ما يسمح به فعليًا دستور الـ data URI، وفيه الشرطات المائلة والنقطتان وعلامات المساواة والاقتباس والأقواس التي يعجّ بها الترميز. هنا يفوز ترميز النسبة المئوية، ولهذا يُرمَّز ملف SVG مكتوب بعلامة اقتباس مفردة حول سماته أفضل بكثير من ملف مكتوب بعلامات اقتباس مزدوجة.
- وتضيف الوجهة قاعدة واحدة أخرى. فداخل سمة HTML تبدأ علامة العطف المجردة مرجع محرف، ولذلك يؤدي اختيار وجهة img إلى تهريبها هي أيضًا. ولا يتغير شيء سواها: علامة الاقتباس المزدوجة والقوسان الزاويان والشرطة المائلة العكسية خارج المجموعة المسموح بها أصلًا وتُهرَّب في المستويين.
- وعلامة النسبة المئوية تُهرَّب دائمًا، في كل مستوى. فهي تفتتح متتالية هروب، ولذلك يجب كتابة العلامة الحرفية %25 وإلا قُرئ المحرفان التاليان على أنهما ست عشريان.
وبدل أن تُترك للتخمين، ترمّز الصفحة الحمولة بالطريقتين وتُبلغ بالطولين وبالفارق بينهما. اختر الفائز، أو حدّد الترميز يدويًا إن كان لديك سبب.
نسبة الـ 33% رقم ما قبل الضغط
يكاد كل نقاش حول الـ data URI يكرر أن Base64 يضيف ثلثًا إلى حجم الملف. هذا صحيح حسابيًا ومضلل عمليًا، لأن الرقم يصف البايتات قبل أن تصل إلى الشبكة، وكل خادم يضغط النص في طريق الخروج.
لا يضيف Base64 معلومات؛ إنما ينشر المعلومات نفسها على محارف أكثر، مستخدمًا 64 قيمة فقط من 256 قيمة يمكن للبايت أن يحملها. وهذا بالضبط نوع الحشو الزائد الذي وُجد gzip وBrotli لإزالته. ومع حمولة مضغوطة أصلًا — صورة PNG أو JPEG أو خط WOFF2 — لا يستطيع الضاغط أن يمسّ البيانات الأساسية لكنه يستطيع أن يعيد ضغط Base64 إلى حجم قريب جدًا من الأصل. فيختفي ذلك الثلث الشهير في معظمه.
ولهذا تقيس هذه الصفحة بدل أن تؤكد. فهي تضغط ثلاثة أشياء بمحرك gzip الخاص بالمتصفح: الحمولة كما كانت ستُقدَّم كملف منفصل، والمستند الحامل لها بترميز Base64، والمستند الحامل لها بترميز النسبة المئوية. هذه الأرقام الثلاثة هي النسخة الصادقة من حجة الحجم، وهي تُظهر كثيرًا أن الكلفة الحقيقية للتضمين ليست الحجم أصلًا.
الكلفة الحقيقية في مكان آخر. الملف المضمَّن لا يمكن تخزينه مؤقتًا بمفرده، فيُنزَّل مجددًا مع كل نسخة من المستند الذي يحمله، ولا يمكن أبدًا مشاركته بين صفحتين. وهو يعطّل أيضًا: فورقة الأنماط لا تنتهي من التحليل حتى تقرأ العنوان الكامل الجالس في وسطها. وقد أزال HTTP/2 معظم كلفة الطلب الواحد التي جعلت التضمين جذابًا في الأصل. أما لأيقونة صغيرة تظهر في كل صفحة فلا تزال مقايضة معقولة؛ وبعد بضع عشرات الكيلوبايتات لم تعد كذلك عادةً، ولهذا تبدأ هذه الصفحة بالتحذير عند الثلاثين.
النوع يأتي من البايتات
لا يفيد الـ data URI إلا إذا كان نوع الوسائط صحيحًا. أخطئ فيه ويرفض المتصفح عرض الصورة، أو يشغّل المحلل الخطأ، أو — مع أي شيء يُقدَّم بنوع لا يعرفه — يعرض تنزيلًا بدلًا من ذلك.
حين تُسقط ملفًا، يبلّغ المتصفح عن نوع خاص به، لكن هذا التبليغ مشتق من امتداد الملف ولا شيء غيره. أعد تسمية صورة JPEG إلى .png وسيسمّيها المتصفح PNG. أما هذه الصفحة فتقرأ البايتات الأولى بدلًا من ذلك. فكل صيغة ثنائية تقريبًا تبدأ بتوقيع: تبدأ PNG ببايت لا يمكن أن يظهر في ASCII تليه الحروف PNG، وتبدأ JPEG بثلاث بايتات ثابتة، وPDF بعلامة نسبة مئوية وكلمة PDF، وWOFF وWOFF2 بوسميهما الرباعيين. وحين يتناقض التوقيع والامتداد، يفوز التوقيع ويُبلَّغ عن التناقض.
والقائمة قصيرة عن قصد — الصيغ التي يضمّنها الناس فعلًا، لا كل ما يستطيع متصفح استشعاره. فإن لم يطابق شيء وكانت البايتات نصًا صالحًا، فالنوع نص، مع فصل SVG عن XML بالنظر إلى ما يفتتح به الترميز. وإن لم يطابق شيء البتة فالجواب application/octet-stream، ويبقى الحقل قابلًا للتحرير، لأن تخمينًا خاطئًا أسوأ من اعتراف صريح.
وللحمولات النصية يهمّ ترميز المحارف أيضًا. فالافتراض في المواصفة هو US-ASCII، وليس هذا ما يقصده أحد اليوم، ولذلك يُعرض نوع الوسائط النصي ومعه charset=utf-8. وحذفه لا يفسد البايتات لكنه يغيّر كيفية قراءتها لاحقًا.
قراءة واحد بالعكس
النصف الآخر من العمل هو الذي يتكرر أكثر: وجدت data: URI في ورقة أنماط أو نسخة من شجرة الصفحة أو صفحة محفوظة، وتريد أن تعرف ما هو. الصقه وتفكّكه الصفحة — نوع الوسائط، ومعاملاته، وأي ترميز استُخدم، وكم بايتًا يفكّ إليه مقابل كم محرفًا يكلّف، وما تبدو عليه تلك البايتات بمعزل عمّا تدّعيه الترويسة. تُعرض الصورة، ويُعرض النص، وأي شيء آخر يحصل على أول بايتاته بالنظام الست عشري.
وفك الترميز صارم، لأن العنوان الملصوق مدخل غير موثوق، ولأن جوابًا معقولًا لكنه خاطئ أسوأ من خطأ صريح. فاصلة ناقصة، نوع وسائط معطوب، علامة نسبة مئوية لا يليها رقمان ست عشريان، محرف خارج أبجدية Base64: كل واحد منها يفشل مع موضع المشكلة بدل أن يُرقَّع في صمت.
ويُتسامح مع أمرين، لأن كل متصفح يتسامح معهما ولأن عنوانًا منسوخًا من ورقة أنماط ملفوفة سيكون بغير ذلك غير قابل للاستعمال. تُزال المسافات داخل الحمولة، ويُستكمل حشو Base64 الناقص. ويُبلَّغ عن الأمرين، لتعرف أن العنوان الذي بين يديك ليس تمامًا العنوان الذي يعمل في كل مكان.
وأمر واحد يبدو مشابهًا يُرفض من أساسه. أبجدية Base64 الآمنة للعناوين، التي تستبدل الزائد والشرطة المائلة بالشرطة والشرطة السفلية، هي ما تستخدمه رموز JSON Web Token — وليست ما يقبله الـ data URI. وقبولها بصمت يعني فكّها إلى بايتات غير التي يفكّها المتصفح، ولذلك تُرفض بالاسم، مع موضع المحرف المخالف.
أين يعمل هذا
كل شيء يحدث داخل متصفحك. الملف الذي تختاره يُقرأ بواجهة الملفات المحلية ولا يُرفع أبدًا؛ والترميز وقياس الضغط وفك الترميز تجري كلها على جهازك، ولا يُخزَّن شيء ولا يُسجَّل. وهذا يهم هنا أكثر من معظم الصفحات، لأن الملفات التي يحوّلها الناس إلى data URI غالبًا أصول داخلية، ولأن العناوين التي يلصقونها للفحص تأتي من صفحات إنتاج.
الأسئلة الشائعة
- هل يُرفع ملفي إلى أي مكان؟
- لا. يُقرأ الملف محليًا بواجهة الملفات في المتصفح، ويُرمَّز على جهازك، ولا يُرسل إلى أي مكان. والأمر نفسه ينطبق على عنوان تلصقه لفك ترميزه.
- هل أستخدم Base64 أم ترميز النسبة المئوية؟
- الأقصر لحمولتك، وهو ما تقيسه الصفحة نيابة عنك. وكقاعدة عامة: ترميز النسبة المئوية لملفات SVG وCSS وJSON وسائر النصوص، وBase64 للصور والخطوط وكل ما هو مضغوط أصلًا. والفارق كثيرًا ما يبلغ عشرين إلى ثلاثين في المئة في أي من الاتجاهين.
- هل يكبّر Base64 الملف بنسبة 33% فعلًا؟
- قبل الضغط، نعم: أربعة محارف لكل ثلاث بايتات. وعلى الشبكة، لا في الغالب. يستخدم Base64 64 قيمة فقط من 256 قيمة ممكنة للبايت، وهذا الحشو الزائد هو بالضبط ما يزيله gzip، فيكون الحجم المضغوط قريبًا عادةً من الحجم المضغوط للملف الأصلي. وتقيس الصفحة الاثنين لتراه بنفسك.
- ما أقصى حجم لـ data URI؟
- لا تفرض المتصفحات الحديثة حدًا صارمًا على عنوان داخل مستند، وإن كانت إصدارات قديمة من Internet Explorer تحدّه بـ 32 كيلوبايت. الحجم مسألة أداء لا مسألة قانون: فالملف المضمَّن لا يمكن تخزينه مؤقتًا بمفرده ويُنزَّل من جديد مع كل نسخة من المستند. وترمّز هذه الصفحة حتى ميغابايت واحد وتحذّر فوق ثلاثين كيلوبايت.
- لماذا ينكسر عنوان الـ SVG عندي داخل CSS؟
- في الغالب محرف لم يُهرَّب. فعلامة المربع — من لون مثل #2563eb — تفتتح معرّف جزء وتقطع العنوان عند تلك النقطة. وعلامة الاقتباس المزدوجة تنهي سلسلة CSS التي تقع داخلها. اكتب ملف SVG بعلامات اقتباس مفردة حول سماته واستخدم مجموعة الهروب الدنيا، فهي تُهرّب علامة المربع وعلامة الاقتباس المزدوجة وتترك ما عداهما قصيرًا.
- هل أستطيع استعمال data URI لسكربت أو إطار مضمَّن؟
- تستطيع، لكن عامله كمسألة أمنية لا كمسألة راحة. فالـ data URI لا يرث أصلًا، وقد صارت المتصفحات تمنع التنقل الأعلى إليه لهذا السبب بالذات. وسياسة أمان محتوى تسمح بـ data: في script-src أو frame-src تتنازل عن معظم ما جاءت السياسة لحمايته؛ أما السماح به في img-src أو font-src فأمر اعتيادي.
- لماذا رُفض ترميز Base64 الآمن للعناوين لدي؟
- لأن الـ data URI يقبل Base64 القياسي. الأبجدية الآمنة للعناوين تستبدل الزائد والشرطة المائلة بالشرطة والشرطة السفلية، وهما محرفان مختلفان يفكّان إلى بايتات مختلفة. وقبولهما يعني أن هذه الصفحة تفكّ عنوانك على نحو يخالف المتصفح الذي سيحمّله فعلًا.
- يقول المتصفح إن ملفي من نوع وتقول هذه الصفحة نوعًا آخر. أيهما الصحيح؟
- الصفحة. يبلّغ المتصفح عن النوع الذي يستنتجه من امتداد الملف، فيخطئ في ملف أُعيدت تسميته. أما هذه الصفحة فتقرأ التوقيع في بداية الملف، وهو ما تعلنه الصيغة نفسها. ويبقى الحقل قابلًا للتحرير إن كان لديك سبب لتجاوزه.