تمهيد
صيغة WebP هي صيغة صور حديثة مفتوحة المصدر ومطوَّرة من Google في عام 2010 بناءً على صيغة الفيديو VP8. منذ ذاك الحين، ازداد عدد مواقع الويب (وتطبيقات الهاتف) التي تستخدم صيغة WebP ازديادًا كبيرًا. يدعم متصفحا Google Chrome و Opera صيغة WebP دون إضافات، وهذان المتصفحات مسؤولان عن عرض 74% تقريبًا من صفحات الويب، مما يضمن أنَّ عددًا كبيرًا من المستخدمين يمكنهم تصفح المواقع بشكل أسرع فيما لو استعملت تلك المواقع صورًا بصيغة WebP؛ يجدر بالذكر أنَّ هنالك خططٌ لإضافة دعم لصيغة WebP في متصفح Firefox.
تدعم صيغة WebP ضغط الصور الفقود (lossy) وغير الفقود (lossless)، وتدعم أيضًا الرسومات المتحركة، لذا يمكنها أن تستبدل صيغة GIF. ميزتها الرئيسية التي تتفوق فيها على صيغة الصور الأخرى هي أنَّ الحجم التخزيني للصورة أقل بكثير، مما يعني أنَّ سرعة تحميل الصور ستكون أكبر، وسيقل تراسل البيانات في الخادم وعند زائر الموقع. ولمّا كانت الصور بصيغة WebP أقل حجمًا بنسبةٍ كبيرة مقارنةً مع صور PNG و JPEG، فإنَّها ستسرِّع تحميل الصفحات بنسبةٍ كبيرة. إذا كان تطبيق الويب أو الموقع الخاص بك يعاني من مشاكل في الأداء أو كان حجم التراسل الشبكي كبيرًا، فتحويل صورك إلى صيغة WebP سيساعدك كثيرًا في تحسين أداء الصفحات.
سنستخدم في هذه المقالة أداةً تعمل من سطر الأوامر باسم cwebp
لتحويل الصور إلى صيغة WebP، وسنُنشِئ سكربتات تسمح لنا بتحويل جميع الصور الموجودة في مجلّد مُحدَّد إلى هذه الصيغة، بما في ذلك الصور المضافة حديثًا. وفي النهاية، سنناقش منهجيتين يمكننا اتباعهما لتخديم صور WebP إلى زوار موقعنا.
المتطلبات المسبقة
أغلبية الخطوات والأوامر في هذه المقالة تنطبق على جميع توزيعات لينكس، والاختلافات تكون في طريقة تثبيت البرامج وحسب؛ لذا سنعتمد على ذكر طريقة تثبيت الأدوات اللازمة على توزيعة أوبنتو 16.04 و CentOS 7. ما يلزم هو خادم قد ثبتنا عليها إحدى التوزيعتين السابقتين، ومضبوطٌ فيه خادم أباتشي، مع وحدة mod_rewrite
، لاحظ أنَّ حزمة أباتشي في توزيعة CentOS 7 تأتي مع دعم mod_rewrite
مسبقًا، أما أوبنتو فهي تحتاج إلى تفعيل.
لاحظ أنَّ الغالبية العظمى من الأوامر في هذا الدرس ستعمل على التوزيعات الأخرى دون تعديل.
بعد حصولك على وصول إلى الخادم، وتثبيت أباتشي وتفعيل وحدة mod_rewrite
، فيمكنك المتابعة مع هذه المقالة.
الخطوة الأولى: تثبيت cwebp وتهيئة مجلد الصور
سنثبِّت في هذا القسم البرمجات التي سنستخدمها لتحويل الصور، وسنُنشِئ أيضًا مجلدًا وننزِّل فيه صورًا لنختبر الأوامر عليها.
لتثبيت cwebp
، البرمجية التي تضغط الصور وتحوِّلها إلى صيغة .webp
، فسنستخدم الأمر الآتي:
أما إذا كنتَ تستعمل CentOS 7، فيمكنك استخدام الأمر الآتي بدلًا من الأمر السابق:
سنُنشِئ مجلدًا جديدًا في المجلد الجذر لخادم أباتشي (والموجود في المسار /var/www/html
) باسم webp
:
سنغيّر ملكية هذا المجلد إلى المستخدم سامي (sammy
، بفرض أنَّ لدينا في النظام مستخدمٌ بذاك الاسم، وإن لم يكن متاحًا فيمكنك إنشاؤه إن أردت، فهو أفضل من ترك الملفات بملكية الجذر [المستخدم root
]) وذلك باستخدام الأمر الآتي:
سنحتاج إلى بعض الصور لتجربة الأوامر عليها، لذا سنُنزِّل صورًا حرةً باستخدام الأمر wget
. لاحظ أنَّ هذه الأداة مثبتة افتراضيًا في أوبنتو 16.04، لكن تثبيتها سهلٌ جدًا في CentOS 7:
ملاحظة: لتسهيل متابعة الأوامر الموجودة في هذه المقالة، سنُنزِّل ثلاث صور حرة من الإنترنت، أوّل صورتان منها (Junonia orithya و Mycalesis junonia) مرخصتان برخصة CC BY-SA 4.0 لصاحبها Jee & Rani Nature Photography؛ أما الصورة الأخيرة (Dental Care) فهي مرخصة برخصة CC0.
معظم عملنا سيكون داخل المجلد /var/www/html/webp
، لذا سنغيّر مجلد العمل الحالي إليه بكتابة الأمر:
بعد أن ثبتنا خادم أباتشي، وفعّلنا إضافة mod_rewrite
، وثبتنا cwebp
وبعد أن ضبطنا مجلد التجارب، يمكننا الآن أن نتابع مع الخطوات القادمة.
الخطوة الثانية: ضغط الصور باستخدام cwebp
قبل أن نناقش كيفية تخديم صور .webp
إلى زوار موقعنا، علينا أن نُنشِئ نسخًا بهذه الصيغة من الصور الموجودة لدينا حاليًا. يمكننا استخدام الأداة cwebp
لتحويل صور JPEG أو PNG أو TIFF إلى صياغة .webp
. الصيغة العامة لهذا الأمر تبدو كما يلي:
يمكن تحديد مسار ملف المخرجات (بصيغة .webp
) بعد الخيار -o
، لاحظ أنَّ صور WebP تنتهي عادةً باللاحقة .webp
.
يمكننا ضبط جودة الصورة -q
إلى أيّ قيمة بين 0 و 100؛ وستكون القيمة الافتراضية، إن لم نضبطها، مساويةً إلى 75.
إذا نزّلتَ الصور التي ذكرناها في الخطوة الأولى، فيمكنك تنفيذ الأوامر الآتية لتحويل الصورة image1.jpg
إلى image1.webp
و image2.jpg
إلى image2.webp
. تذكّر أن تغيّر مجلد العمل الحالي إلى /var/www/html/webp
، ثم نفِّذ الأمر cwebp
لتحويل الصور بجودة 100%:
لنلقِ نظرةً على الحجم التخزيني لملفات JPEG و WebP باستخدام الأمر ls
. سنستخدم الخيار -l
لعرض الصيغة التفصيلية التي تتضمن حجم الملف التخزيني، والخيار -h
الذي يطلب من الأمر ls
أن يعرض الحجم بصيغةٍ سهلة القراءة:
سيُنتِج الأمر السابق الناتج الآتي:
يُظهِر ناتج الأمر ls
أنَّ حجم الملف image1.jpg
هو 7.4 ميغابايت، بينما حجم الملف image1.webp
هو 3.9 ميغابايت؛ والمثل ينطبق على ملف image2.jpg
(16 ميغابايت) و image2.webp
(7.0 ميغابايت). لاحظ أنَّ حجم الملفات قد أصبح نصف ما كان عليه!
إذا كنّا نجري ضغطًا غير فقود (lossless، مما يعني أنَّ البيانات الأصلية للصورة ستُحفَظ كاملةً دون أيّ ضياع أثناء عملية الضغط)، فيمكننا أن نستعمل الخيار -lossless
، الذي يُعدّ أفضل طريقة للحفاظ على جودة صور PNG. إذا كانت لديك صور بصيغة PNG (كالتي نزّلناها في الخطوة السابقة) وتريد تحويلها إلى صيغة WebP لتقليل حجمها التخزيني، فيمكنك أن تستعمل الأمر الآتي:
الأمر الآتي يُظهِر أنَّ الحجم التخزيني لصورة WebP هو 60 كيلوبايت، وهو حوالي نصف الحجم التخزيني لصورة PNG (حوالي 116 كيلوبايت).
ناتج الأمر السابق:
الخلاصة هي أنَّ الحجم التخزيني لنسخة WebP من الصور هي أقل بحوالي 50% من الحجم التخزيني لمثيلاتها من JPEG و PNG، لكن لاحظ أنَّ نسبة الضغط قد تختلف اعتمادًا على الصور التي عندك. يجدر بالذكر أنَّ هنالك عوامل أخرى كثيرة تؤثر على معدّل الضغط، بما في ذلك معدّل الضغط للصورة الأصلية، وصيغة الملف الأصلي، وطريقة التحويل (فقودة أم غير فقودة)، والجودة المطلوبة، ونظام التشغيل الذي تجري عليه عملية التحويل.
الخطوة الثالثة: تحويل جميع صور JPEG و PNG في أحد المجلدات
لقد حوّلنا بعض الملفات في الخطوة السابقة إلى صيغة WebP يدويًا، لكن فعل ذلك يأخذ وقتًا طويلًا ويتطلب جهدًا كثيرًا. يمكننا تبسيط هذه العملية بكتابة سكربت تحويل الذي سيبحث عن ملفات JPEG ويحولها إلى صيغة WebP بجودة 90%، وسيحوِّل أيضًا صور PNG إلى صور WebP غير فقودة.
أوّل عنصر في السكربت الذي سنكتبه هو الأمر find
. يمكنك تعلّم المزيد من المعلومات حول الأمر find
من الفصل السابع عشر «البحث عن الملفات» من كتاب «سطر أوامر لينكس». سيبحث الأمر find
عن الملفات في المجلد المُحدَّد، وسنستخدم معه بعض الخيارات لمطابقة أسماء تلك الملفات.
لنُنشِئ السكربت باستخدام المحرر النصي المُفضَّل لدينا. سنستخدم هاهنا المحرر nano
لكنك تستطيع أن تستعمل أيّ محرر ترتاح معه. سنُنشِئ الملف webp-convert.sh
في مجلد /var/www/html/webp
:
سيبدو أوّل سطر من الملف كما يلي:
للحصول على مسار المجلد الذي يحتوي على الصور التي نريد تحويلها، فسنستخدم المعامل الموضعي $1
. لاحظ أنَّنا سنُمرِّر مسار مجلّد الصور التي نريد تحويلها إلى السكربت من سطر الأوامر، وهنا سيأتي دور المعامل الموضعي السابق. الغرض من استخدامه هو القدرة على تحديد مسار مجلد الصور بغض النظر عن مكان تخزين السكربت.
باستخدامنا للخيار -type f
أخبرنا الأمر find
أن يبحث عن الملفات العادية فقط (أي أننا لا نريد أن نحصل على مسارات المجلدات في نواتجه). نريد أيضًا أن نُطابِق اسم الملف مع نمطٍ معيّن باستخدام الاختبار -iname
. لاحظ أنَّنا استخدمنا اختبارًا غيرَ حساسٍ لحالة الأحرف (وذلك باستخدامنا للاختبار -iname
بدلًا من -name
)، وهذا الاختبار سيبحث عن أيّة ملفات تنتهي باللاحقة .jpg
(*.jpg
) أو .jpeg
(*.jpeg
). رمز النجمة *
هو محرفٌ خاصٌ يُطابِق محرفًا عاديًا صفر مرة أو أكثر. لاحظ أيضًا أنَّنا استخدمنا المعامل المنطقي -o
(الذي يعني «أو») لنطلب من الأمر find
أن يعرض الملفات التي تُطابِق الاختبار الأول -iname
(أي -iname "*.jpg"
) أو الاختبار الثاني (-iname "*.jpeg"
). وضعنا كلا الاختبارين بين قوسين لنحرص على أنَّ الاختبار الأول (أي -type f
) سيُنفَّذ دومًا باستخدام المعامل المنطقي -and
.
بعد العثور على الملفات المطلوبة، علينا أن نتخذ إجراءً لتحويلها إلى صيغة WebP. سنفعل ذلك باستخدام المعامل -exec
في السطر الثاني من السكربت الذي نعمل عليه (لاحظ أنَّنا استخدمنا الشرطة المائلة الخلفية \
للسماح بامتداد الأمر find
على أكثر من سطر). البنية العامة لهذا المعامل هي -exec command {} \;
. السلسلة النصية {}
ستُستبدل بمسار كل ملف يعثر عليه الأمر find
(أي أنَّ الأمر المُحدَّد بهذا المعامل سيُنفَّذ على جميع الملفات، كلًا على حدة). استخدمنا ;
لنخبر الأمر find
أين ينتهي الأمر (لاحظ أنَّنا هربنا [escape] المحرف ;
لأنَّ له معنى خاص في الصدفة [shell] bash، وهو الإشارة إلى نهاية الأوامر).
علينا أن نتحقق أولًا من عدم وجود نسخة .webp
من الصور وذلك لكل صورة يُعثر الأمر find
عليها، وإن لم تكن موجودةً فسنحوِّلها باستخدام الأمر cwebp
كما ناقشنا في الخطوة السابقة. السؤال الآن هو ما هو الأمر الذي علينا تنفيذه مع المعامل -exec
؟ صراحةً، سنحتاج إلى تنفيذ أكثر من أمر واحد لتحويل الصور إلى .webp
. ولهذا السبب، سنستخدم الأمر bash
لتنفيذ سكربت صغير الذي سيُنشِئ نسخة .webp
من الملف إن لم تكن موجودةً. ولفعل ذلك سنُمرِّر هذا السكربت الصغير كسلسلة نصية (محاطة بعلامتي اقتباس) باستخدام الخيار -c
. إلى هذه المرحلة، لقد تضمّن الأمر find
المعامل -exec
، الذي يُنفِّذ الأمر bash
الذي سنُمرِّر إليه سكربتًا صغيرًا (يُمثَّل فيما يلي بالمحتوى النائب `'commands') الذي سيُنفَّذ مجموعة أوامر على ملفات JPEG. يجب أن يبدو الملف الذي نعمل عليه كما يلي في هذه المرحلة:
للحفاظ على طاقة المعالجة في الخادم، فسنختبر إن كان هنالك ملفٌ محوّلٌ مسبقًا إلى صيغة WebP، وذلك باستخدام الأمر if
. سنختبر [
إن لم يكن !
هنالك ملفٌ -f
باسم "$webp_path"
موجودًا مسبقًا. لاحظ وجود علامتي اقتباس حول اسم الملف لتفادي حدوث مشاكل فيما لو احتوى على فراغات:
في النهاية، إذا لم يكن الملف موجودًا، فسنستخدم الأمر cwebp
لإنشائه، كما فعلنا عند تحويل الصور في مجلد webp
في الخطوة السابقة:
يجب أن يبدو السكربت المستخدم لتحويل صور JPEG كما يلي عند هذه المرحلة:
أما لتحويل صور PNG، فسنستعمل شيئًا شبيهًا جدًا بما فعلناه مع صور JPEG، باستثناء أمرين اثنين: أولهما هو تغيير النمط المستعمل في الأمر find
إلى "*.png"
، وثانيهما هو استعمال الخيار -lossless
في الأمر cwebp
لتحويل صور PNG بطريقة غير فقودة (كما ناقشنا في الخطوة الثانية).
سيبدو الملف كله كما يلي:
find $1 -type f -and ( -iname ".jpg" -o -iname ".jpeg" )
-exec bash -c '
webp_path=$(sed 's/.[^.]*$/.webp/' <<< "$0");
if [ ! -f "$webp_path" ]; then
cwebp -quiet -q 90 "$0" -o "$webp_path";
fi;' {} ;
find $1 -type f -and -iname ".png"
-exec bash -c '
webp_path=$(sed 's/.[^.]$/.webp/' <<< "$0");
if [ ! -f "$webp_path" ]; then
cwebp -quiet -lossless "$0" -o "$webp_path";
fi;' {} ;
</div>
لنختبر الآن السكربت `webp-convert.sh`، سنستخدم الملفات التي أنشأناها في الخطوة السابقة لتجربته. لكن احرص أولًا على جعل ملف السكربت قابلًا التنفيذ:
<div markdown="1" dir="ltr">
chmod a+x ~/webp-convert.sh
</div>
لنشغِّل السكربت على المجلد `/var/www/html/webp`:
<div markdown="1" dir="ltr">
./webp-convert.sh /var/www/html/webp
</div>
لم يحدث شيء! ذلك لأنَّ السكربت بحث عن وجود نسخة `.webp` من الصور في المجلد `/var/www/html/wepb`، لكننا حولناها كلها في الخطوة الثانية من هذا الدرس. علينا إذًا أن نحصل على صورٍ جديدةٍ أو نحذف نسخة `.webp`. الأمر الآتي سيحذف جميع ملفات `.webp` الموجودة في المجلد `/var/www/html/webp`:
<div markdown="1" dir="ltr">
rm /var/www/html/webp/*.webp
</div>
بعد حذف جميع صور `.webp`، يمكننا تشغيل السكربت مرةً أخرى للتحقق أنَّه يعمل:
<div markdown="1" dir="ltr">
./webp-convert.sh /var/www/html/webp
</div>
سيؤكد الأمر `ls` أنَّ السكربت قد حوّل الصور بنجاح:
<div markdown="1" dir="ltr">
ls -lh /var/www/html/webp
</div>
الناتج:
<div markdown="1" dir="ltr">
-rw-r--r-- 1 sammy sammy 7.4M Oct 28 23:36 image1.jpg -rw-r--r-- 1 sammy sammy 3.9M Feb 18 16:46 image1.webp -rw-r--r-- 1 sammy sammy 16M Dec 18 2016 image2.jpg -rw-r--r-- 1 sammy sammy 7.0M Feb 18 16:59 image2.webp -rw-r--r-- 1 sammy sammy 116K Jul 18 2017 logo.png -rw-r--r-- 1 sammy sammy 60K Feb 18 16:42 logo.webp
</div>
هذه الخطوة هي أساس استخدام صور WebP في موقعنا، لأننا نحتاج إلى وجود نسخة WebP من جميع الصورة في موقعنا. سننظر في الخطوات القادمة كيف يمكننا الاستفادة من تلك الصور لتسريع مواقع الويب!
## الخطوة الرابعة: مراقبة ملفات الصور في أحد المجلدات
حوّلنا جميع الصور عندنا إلى صيغة `.webp` في الخطوة الثالثة، لكننا قد نحتاج إلى أتمتة هذه الخطوة للتعامل مع الصور الجديدة التي لم تحوَّل إلى WebP بعد. يمكننا إضافة سكربت جديد لتحويل لمراقبة التغييرات التي تحصل في مجلد الصور لتحويل الصور الجديدة مباشرةً دون الحاجة إلى تشغيل سكربت `webp-convert.sh` يدويًا في كل مرة نضيف فيها صورةً جديدةً.
إحدى المشاكل في سكربت `webp-convert.sh` هي أننا لا نستطيع أن نعرف أنَّ صورةً ما قد أُعيدَت تسميتها أو حُذِفَت. فمثلًا، لو أنشأنا صورةً باسم `foo.jpg` وشغّلنا سكربت `webp-convert.sh`، ثم أعدنا تسمية تلك الصورة إلى `bar.jpg` وشغّلنا السكربت `webp-convert.sh` مجددًا، فسينتهي بنا المطاف بوجود ملفي `.webp` متطابقين (وهما `foo.webp` و `bar.webp`). لهل هذه المشكلة، ولتفادي تشغيل السكربت يدويًا، فسنُضيف مراقبات (watchers) التي تراقب ملفاتٍ أو مجلداتٍ معيّنة للتغييرات، وتُشغِّل أوامر ردًا على تلك التغييرات.
يمكننا ضبط تلك المراقبات باستخدام الأمر `inotifywait`، الذي هو جزءٌ من الحزمة `inotify-tools`، وهي مجموعةٌ من الأوامر السطرية التي توفِّر واجهةً بسيطةً لنظام inotify في نواة نظام لينكس. يمكننا تثبيت هذه الحزمة في نظام أوبنتو بالأمر الآتي:
<div markdown="1" dir="ltr">
sudo apt-get install inotify-tools
</div>
أما لتوزيعة CentOS 7، فالحزمة `inotify-tools` متوافرة في مستودع EPEL. يمكننا تثبيت مستودع EPEL وتثبيت الحزمة `inotify-tools` باستخدام الأمرين الآتيين:
<div markdown="1" dir="ltr">
sudo yum install epel-release sudo yum install inotify-tools
</div>
الأمر `inotifywait` ينتظر حدوث تغييرات في مجلدٍ معيّن. الخيار `-q` سيخبر الأمر `inotifywait` ألّا يخرج الكثير من المخرجات. نرغب أيضًا أن يعمل الأمر `inotifywait` دون أجلٍ مسمى (`-m`) كيلا ينتهي تنفيذه بعد الحصول على أوّل حدث وقع في المجلد. إضافةً إلى ذلك، نرغب بضبط المراقبات في المجلد المُحدَّد وجميع المجلدات الفرعية التابعة له تعاوديًا باستخدام الخيار `-r`.
الصيغة التي نريد الحصول عليها عند وقوع تغييرٍ في المجلد هي «اسم الحدث» متبوعًا بمسار الملف، ويمكننا تغيير الصيغة باستخدام الخيار `--format`. الأحداث التي نريد مراقبتها هي `close_write` (الذي سيُطلَق عند إنشاء ملف وانتهاء عملية كتابته إلى القرص)، و `moved_from` و `moved_to` (التي ستُطلَق عند نقل ملف)، والحدث `delete` (الذي سيُطلَق عند حذف ملف).
يمكننا إنشاء السكربت بمحررنا النصي المُفضَّل كما فعلنا في الخطوة السابقة مع السكربت `webp-convert.sh`. الأمر الآتي سيُنشِئ الملف `webp-watchers.sh` في المجلد `/var/www/html/webp`:
<div markdown="1" dir="ltr">
nano ~/webp-convert.sh
</div>
سيبدو أوّل سطرٍ في السكربت كما يلي:
<div markdown="1" dir="ltr">
inotifywait -q -m -r --format '%e %w%f' -e close_write -e moved_from -e moved_to -e delete $1
</div>
لتفحص ما هي المخرجات التي سيُظهرها هذا الأمر، فنحاول تشغيله في المجلد `/var/www/html/webp`، مع تشغيله في الخلفية بإضافة الرمز `&` في نهاية الأمر. سننتقل الآن من تعديل السكربت إلى سطر الأوامر لاختبار سلوك الأمر `inotifywait` (احرص على حفظ الملف والخروج من المحرِّر):
<div markdown="1" dir="ltr">
inotifywait -q -m -r --format '%e %w%f' -e close_write -e moved_from -e moved_to -e delete /var/www/html/webp &
</div>
لمعرفة المخرجات التي يُنتِجها الأمر `inotifywait`، فسنُنشِئ عدِّة ملفات ونجري عمليات عليها. سنُنِشئ أولًا ملفًا جديدًا فارغًا باسم `new_file` باستخدام الأمر `touch`. أدخِل الأمر الآتي في سطر الأوامر:
<div markdown="1" dir="ltr">
touch /var/www/html/webp/new_file
</div>
ثم سنُعيد تسمية الملف (أي ننقله) إلى `moved_file` باستخدام الأمر `mv`:
<div markdown="1" dir="ltr">
mv /var/www/html/webp/new_file /var/www/html/webp/moved_file
</div>
وفي النهاية سنحذف الملف `moved_file` باستخدام الأمر `rm`:
<div markdown="1" dir="ltr">
rm /var/www/html/webp/moved_file
</div>
سنشاهد ناتجًا شبيهًا بالناتج الآتي في الطرفية:
<div markdown="1" dir="ltr">
CLOSE_WRITE,CLOSE /var/www/html/webp/new_file MOVED_FROM /var/www/html/webp/new_file MOVED_TO /var/www/html/webp/moved_file DELETE /var/www/html/webp/moved_file
</div>
سيظهر أوّل سطر بعد إنشاء الملف الجديد، وسيظهر السطران الثاني والثالث بعد نقل الملف (ويشيران إلى مسار المصدر والوجهة). في النهاية، آخر سطر يُظهِر أنَّ الملف قد حُذِف.
بالعودة إلى أوّل سطر في سكربت `webp-watchers.sh`، سنستخدم الأمر `grep` لمعرفة إن كانت الملفات التي أطلقت أحد الأحداث السابقة هي ملفات بصيغة JPEG أو PNG؛ ولمّا كان الأمر `inotifywait` لا يوفِّر الخيار `--include` لمراقبة الملفات التي تُطابِق تعبيرًا نمطيًا محدَّدًا، فنحن مجبرون لاستخدام الأمر `grep` لحلّ التفافي لهذا القصور. أمر `grep` الآتي سيؤدي إلى مطابقة صور JPEG أو PNG فقط. وسنستخدم فيه الخيار `-i` لتجاهل حالة الأحرف، والخيار `-E` لاستخدام التعابير النمطية الموسّعة (extended regular expressions). سنضيف أيضًا الخيار `--line-buffered` إلى أمر `grep` لتمرير الأسطر التي جرت مطابقتها إلى حلقة التكرار (التي سنُناقشها بعد قليل):
<div markdown="1" dir="ltr">
| grep -i -E '.(jpe?g|png)$' --line-buffered
</div>
أي أنَّ ناتج الأمر `inotifywait` سيُمرِّر مسار الملفات التي تغيّرت في المجلد المُحدَّد عبر أنبوب (pipe، أي الرمز `|`) إلى الأمر `grep`، الذي بدوره سيتحقق إن كان الملف صورةً.
الخطوة الآتية هي بناء حلقة التكرار `while`. يمكننا الاستفادة من حلقة التكرار `while` مع الأمر `read` في حالات مثل حالتنا؛ مما يسمح لنا بمعالجة كل سطر من الأسطر المُمرَّرة إلى الحلقة فور إخراجها. سيُخزِّن الأمر `read` الحدث في متغيرٍ باسم `$operation` وسيخزِّن مسار الملف المُعالَج في متغيرٍ باسم `$path`:
<div markdown="1" dir="ltr">
| while read operation path; do
commandsdone;
</div>
لندمج ذلك مع أمر `inotifywait` الموجود في أوّل سطر من السكربت:
<div markdown="1" dir="ltr">
inotifywait -q -m -r --format '%e %w%f' -e close_write -e moved_from -e moved_to -e delete $1
| grep -i -E '.(jpe?g|png)$' --line-buffered
| while read operation path; do
done;
</div>
حلقة `while` ستتحقق من الحدث المُطلَق من الأمر `inotifywait`، ومن ثم ستجري الأوامر الموجودة داخل الحلقة الأفعال الآتية:
* إنشاء ملف WebP جديد إن أنشأنا صورةً جديدةً أو نقلناها إلى المجلد الهدف.
* حذف ملف WebP إذا حُذِفَت الصورة الأصلية أو نُقِلَت من المجلد الهدف.
هنالك ثلاثة أقسام رئيسية في حلقة التكرار. أولها هو تعريف متغيرٍ باسم `webp_path` الذي هيّأناه ليُخزِّن نسخة `.webp` من الصورة الهدف (أنشأناه بأسلوب مشابه لما فعلناه في الخطوة الثالثة):
<div markdown="1" dir="ltr">
webp_path="$(sed 's/.[^.]*$/.webp/' <<< "$path")";
</div>
الخطوة المنطقية التالية هي اختبار الحدث الذي وقع:
<div markdown="1" dir="ltr">
if [ $operation = "MOVED_FROM" ] || [ $operation = "DELETE" ]; then
commands to be executed if the file is moved or deletedelif [ $operation = "CLOSE_WRITE,CLOSE" ] || [ $operation = "MOVED_TO" ]; then
commands to be executed if a new file is createdfi;
</div>
إذا نقلنا أو حذفنا الملف، فسنتحقق من وجود نسخة `.webp` منه، فإذا كانت موجودةً فسنحذفها باستخدام الأمر `rm`:
<div markdown="1" dir="ltr">
if [ -f "$webp_path" ]; then $(rm -f "$webp_path"); fi;
</div>
أما بالنسبة إلى الملفات المُنشَأة حديثًا، فسنختبر إن كان الملف بصيغة PNG (باستخدام `if` و `grep` كما فعلنا في الخطوة الثالثة). فعلنا لذا سيسمح لنا باستخدام التحويل غير الفقود (عبر الخيار `-lossless`). إذا لم يكن الملف المُطابَق بصيغة PNG فسنجري تحويلًا فقودًا باستخدام نفس الأمر المذكور في الخطوة الثانية (أي بتحديد جودة الصورة عبر المعامل `-q`):
<div markdown="1" dir="ltr">
if [ $(grep -i '.png$' <<< "$path") ]; then $(cwebp -quiet -lossless "$path" -o "$webp_path"); else $(cwebp -quiet -q 90 "$path" -o "$webp_path"); fi;
</div>
قد تتساءل لماذا لم ننقل ملف `.webp` عند نقل الملف الأصلي المرتبط به، ذلك لأننا اتبعنا المنهجية الأسهل: حذف ملف WebP ثم إعادة إنشاءه من الصفر؛ ذلك لأننا لا نريد تعقيد السكربت في حال أضفنا الجزء المسؤول عن نقل نسخة `.webp` المرتبطة بالملفات الأصلية.
يجب أن يكون كامل ملف `webp-watchers.sh` كما يلي:
<div markdown="1" dir="ltr">
#!/bin/bash echo "Setting up watches.";
watch for any created, moved, or deleted image filesinotifywait -q -m -r --format '%e %w%f' -e close_write -e moved_from -e moved_to -e delete $1
| grep -i -E '.(jpe?g|png)$' --line-buffered
| while read operation path; do
webp_path="$(sed 's/.[^.]*$/.webp/' <<< "$path")";
if [ $operation = "MOVED_FROM" ] || [ $operation = "DELETE" ]; then # if the file is moved or deleted
if [ -f "$webp_path" ]; then
$(rm -f "$webp_path");
fi;
elif [ $operation = "CLOSE_WRITE,CLOSE" ] || [ $operation = "MOVED_TO" ]; then # if new file is created
if [ $(grep -i '.png$' <<< "$path") ]; then
$(cwebp -quiet -lossless "$path" -o "$webp_path");
else
$(cwebp -quiet -q 90 "$path" -o "$webp_path");
fi;
fi;
done;
</div>
لا تنسَ أن تجعل السكربت قابلًا للتنفيذ:
<div markdown="1" dir="ltr">
chmod a+x ~/webp-convert.sh
</div>
يمكننا الآن تشغيل السكربت على المجلد `/var/www/html/webp` لمراقبته (وجميع المجلدات الفرعية فيه) للتغيرات. ربما ترغب بتشغيل الأمر في الخلفية بإضافة الرمز `&` في نهاية الأمر:
<div markdown="1" dir="ltr">
./webp-watchers.sh /var/www/html/webp
</div>
في هذه المرحلة، حوّلنا جميع الصور الموجودة في المجلد `/var/www/html/webp` إلى صيغة WebP، وضبطنا المراقبات باستعمال سكربت `webp-watchers.sh`. حان الوقت الآن لاستكشاف خياراتنا بخصوص طريقة تخديم صور WebP إلى زوار مواقعنا.
## الخطوة الخامسة: تخديم ملفات WebP إلى الزوار
إذا اتبعتَ التعليمات الموجودة في الخطوات السابقة، فيجب أن تصبح عندك نسخة `.webp` لكل صورة JPEG أو PNG موجودة في مجلد `/var/www/html/webp`. يمكننا الآن التفكير بطريقة تخديم تلك الصور إلى المتصفحات الداعمة لها.
سنفعل ذلك بمنهجيتين اثنتين: استخدام عناصر HTML5 (العنصر [`<picture>`](http://wiki.hsoub.com/HTML/picture) تحديدًا)، ووحدة `mod_rewrite` في خادم الويب أباتشي. سنناقش طريقة استخدام العنصر `<picture>` في هذه الخطوة، وسنُرجئ شرح الوحدة `mod_rewrite` إلى الخطوة القادمة.
إذا كنتَ مطوِّر (أو صائن) موقعك، فيمكنك استخدام العنصر `<picture>` لتضمين الصور في صفحات الويب في موقعك. يسمح هذا العنصر بتعريف أكثر من مصدر (source) للصورة. وإذا كان المتصفح يدعم صيغة WebP فسيُنزِّل نسخة `.webp` بدلًا من الصورة الأصلية، مما يُسرِّع تخديم الصفحة. يجدر بالذكر أنَّ العنصر `<picture>` مدعومٌ دعمًا جيدًا في المتصفحات الحديثة التي تدعم صيغة WebP.
العنصر `<picture>` هو حاوية التي تحتوي على عناصر [`<source>`](http://wiki.hsoub.com/HTML/source) لتحديد «مصدر» مختلف للصورة المُحدَّدة في العنصر [`<img>`](http://wiki.hsoub.com/HTML/img) المحتوى فيها. إذا استخدمنا العنصر `<source>` للإشارة إلى صورة `.webp`، فسينظر المتصفح إن كان قادرًا على تفسير تلك الصفحة، وإن لم يكن قادرًا فسيستعمل الصورة المُحدَّدة في الخاصية [src](http://wiki.hsoub.com/HTML/img#src) في العنصر `<img>`.
لنقل أنَّ لدينا صورة PNG باسم `logo.png` وحولنا إلى `logo.webp` باستخدام السكربتات السابقة، فيمكننا استخدام شيفرة HTML الآتية لعرض الصورة `logo.webp` لأي متصفح يدعم صيغة WebP، والصورة `logo.png` للمتصفحات الأخرى التي لا تدعم صيغة WebP أو لا تدعم العنصر `<picture>`. لنفتح محررنا النصي المُفضَّل ونُنشِئ ملف HTML في المسار `/var/www/html/webp/picture.html`:
<div markdown="1" dir="ltr">
nano /var/www/html/webp/picture.html
</div>
سنختصر الصفحة الاختبارية إلى البنية الآتية:
<div markdown="1" dir="ltr">
```
يمكن استخدام العنصر <picture>
لتوفير أكثر من نسخة من الصورة، مما يسمح للمتصفح باختيار أكثر صورة ملائمة بينها لعرضها. هذا أبسط خيارٍ متاحٍ أمامنا لتخديم صور .webp
.
بعد أن شرحنا كيفية تخديم صور .webp
مباشرةً من شيفرة HTML، سننتقل الآن إلى شرح طريقة أتمتة هذه العملية باستخدام وحدة mod_rewrite
التابعة لخادم أباتشي.
الخطوة السادسة: تخديم صور WebP باستخدام وحدة mod_rewrite
إذا أردنا تحسين سرعة موقعنا، لكنه يحتوي على عددٍ كبيرٍ من الصفحات أو لدينا وقتٌ وموارد قليلة لتعديل شيفرة HTML، فستساعدنا وحدة mod_rewrite
في أتمتة عملية تخديم صور .webp
إلى المتصفحات الداعمة لها.
أولًا، علينا اختبار إذا كانت وحدة mod_rewrite
متوافرةً، وذلك في ملف .htaccess
باستخدام التعليمة ifModule
؛ وإذا كانت الوحدة متاحةً فسنُفعِّلها باستخدام التعليمة RewriteEngine On
. يمكن إنشاء ملف .htaccess
في مجلد /var/www/html/webp
باستخدام الأمر الآتي:
الصيغة الأوليّة لملف .htaccess
ستكون كما يلي:
لكي نعرف متى علينا تخديم صور .webp
إلى المستخدم، فيجب أن يجري خادم الويب عدِّة اختبارات. فعندما يجري المتصفح طلبيةً، فسيُضمِّن فيها ترويسةً تُخبِر الخادم ماذا يستطيع المتصفح تشغيله. وفي حالة صور WebP، سيُرسِل الخادم ترويسة Accept
التي تحتوي على image/webp
. سنتحقق إذا كان المتصفح قد أرسل هذا الترويسة باستخدام التعليمة RewriteCond
. التعليمة RewriteCond
تُحدِّد المعايير أو الشروط التي يجب أن تُطابَق حتى يُنفَّذ ما في التعليمة RewriteRule
. راجع توثيق وحدة mod_rewrite
الرسمي لمزيدٍ من المعلومات:
علينا ترشيح كل الملفات ما عدا صور JPEG و PNG، وذلك باستخدام تعبيرٍ نمطيٍ شبيهٍ بالتعبير المستخدم في الأقسام السابقة، وذلك لمطابقة عنوان URI المطلوب من المتصفح. لاحظ أننا استخدمنا (?i)
لجعل عملية المطابقة غير حساسة لحالة الأحرف:
لتتحقق أنَّ نسخة .webp
من الملف موجودة (من البدهي أننا لا نريد تخديم صورة غير موجودة إلى المستخدم)، فيمكننا استخدام تعليمة RewriteCond
كما يلي:
في النهاية، إذا تحققت كل الشروط السابقة، فسنُعيد توجيه صورة JPEG أو PNG المطلوبة إلى ملف WebP المرتبط بها. لاحظ أننا «نعيد توجيه الملف» (redirect، أي سيُطلَب من المتصفح أن يجري طلبية جديدة لرابط URI جديد يُشير إلى ملف .webp
) باستخدام الراية R
، ولا «نعيد كتابة رابط URI» (rewrite). الفرق بين إعادة التوجيه وإعادة كتابة رابط URI هي أنَّ المتصفح سيُخدِّم رابط URI الجديد دون إخبار المتصفح بذلك، مما يعني أنَّ الملف المنقول إلى المتصفح هو .webp
لكنّه سيبقى مخدّمًا بنفس رابط URI الأصلي. بعبارةٍ أخرى، سيظهر أنَّ رابط URI يُشير إلى ملف .png
(على سبيل المثال)، لكنه في الحقيقة ملف .webp
:
عند هذه المرحلة، أنهينا قسم mod_rewrite
في ملف .htaccess
، لكن ماذا سيحدث لو كان هنالك خادم تخزين مؤقت بين خادمنا وجهاز المستخدم؟ قد يؤدي ذلك إلى تخديم النسخة الخطأ من الملف إلى الزائر، ولهذا السبب علينا أن نتحقق إذا كانت الوحدة mod_headers
مفعّلةً، وذلك لإرسال ترويسة Vary: Accept
. الترويسة Vary
تُخبر الخوادم الوسيطة (proxy servers) أنَّ نوع المحتوى للمستند (أي MIME type. بعبارةٍ أخرى: صيغة الملف المُخدَّم) تختلف (Vary
) اعتمادًا على قدرات المتصفح الذي طلب الملف. إضافةً إلى ذلك، إنَّ المحتوى المُخدَّم إلى الزائر قد وُلِّد اعتمادًا على ترويسة Accept
الموجودة في الطلبية. فطلبيةٌ أخرى لها ترويسة Accept
مختلفة قد تحصل على ناتج آخر. لاحظ أنَّ إرسال هذه الترويسة هو أمرٌ ضروري لمنع الخوادم الوسيطة من تخديم صور WebP إلى المتصفحات غير الداعمة لها:
في نهاية ملف .htaccess
، سنضبط نوع MIME لملفات .webp
إلى image/webp
باستخدام التعليمة AddType
. فعلنا ذلك لتخديم الصور باستخدام نوع MIME الصحيح:
هذه هي النسخة النهائية من ملف .htaccess
:
AddType image/webp .webp
</div>
ملاحظة: لاحظ أنَّك تستطيع دمج ملف `.htaccess` السابقة مع الملف الموجود مسبقًا عندك. فلو كنتَ تستخدم ووردبريس مثلًا، فيمكنك نسخ ملف `.htaccess` السابق ولصقه في ***بداية*** الملف الموجود عندك.
لنجرِّب ما فعلناه في هذه الخطوة. لو اتبعنا الخطوات السابقة، فيجب أن يكون لدينا ملف باسم `logo.png` و `logo.webp` في مجلد `/var/www/html/webp`. لنجرِّب استخدام عنصر `<img>` بسيط لتضمين صورة الشعار `logo.png` في صفحة الويب. يمكننا إنشاء ملف HTML تجريبي مثلما فعلنا في الخطوة السابقة:
<div markdown="1" dir="ltr">
nano /var/www/html/webp/img.html
</div>
أدخِل شيفرة HTML في ملف `/var/www/html/webp/img.html`:
<div markdown="1" dir="ltr">
```
الخلاصة
تعلمنا في هذه المقالة عن كيفية استخدام صيغة صور WebP لجعل تحميل صفحات الويب أسرع، وشرحنا كيفية استخدام الأداة cwebp
لتحويل الملفات. أوّل طريقة استعملناها لتخديم الملفات هي عبر عنصر <picture>
، أما الخطوة الثانية فكانت باستعمال وحدة mod_rewrite
في أباتشي.
ما شرحناه في هذا الدرس كافٍ لتحقيق هدف تسريع الموقع باستخدام صور WebP، لكن يمكنك تخصيص السكربتات لتناسب احتياجاتك. ننصحك بأخذ وقتك لتصفح المراجع الآتية:
-
لتعلّم المزيد حول ميزات صيغة WebP وكيفية استخدام أدوات التحويل، فانظر إلى توثيق WebP.
-
لمزيدٍ من التفاصيل والأمثلة عن استخدام العنصر
<picture>
، فانظر إلى صفحته في موسوعة حسوب. -
للتوسع في فهم وحدة
mod_rewrite
، فألقِ نظرةً على توثيقها.
استخدام صيغة WebP لصور موقعك سيُقلِّل حجمها التخزيني بنسبةٍ كبيرة، مما يُقلِّل حجم التراسل الشبكي ويجعل تحميل الصفحات أسرع، خصوصًا إذا كان موقعك يحتوي على عددٍ كبيرٍ من الصور.
مصدر هذه المقالة هو مقالة How To Create and Serve WebP Images to Speed Up Your Website المنشورة في DitgitalOcean والتي كتبها عبد اللطيف ايمش.