استخدام صور WebP لتسريع مواقع الويب
تمهيد
صيغة 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
، فسنستخدم الأمر الآتي:
sudo apt-get install webp
أما إذا كنتَ تستعمل CentOS 7، فيمكنك استخدام الأمر الآتي بدلًا من الأمر السابق:
sudo yum install libwebp-tools
سنُنشِئ مجلدًا جديدًا في المجلد الجذر لخادم أباتشي (والموجود في المسار /var/www/html
) باسم webp
:
sudo mkdir /var/www/html/webp
سنغيّر ملكية هذا المجلد إلى المستخدم سامي (sammy
، بفرض أنَّ لدينا في النظام مستخدمٌ بذاك الاسم، وإن لم يكن متاحًا فيمكنك إنشاؤه إن أردت، فهو أفضل من ترك الملفات بملكية الجذر [المستخدم root
]) وذلك باستخدام الأمر الآتي:
sudo chown sammy: /var/www/html/webp
سنحتاج إلى بعض الصور لتجربة الأوامر عليها، لذا سنُنزِّل صورًا حرةً باستخدام الأمر wget
. لاحظ أنَّ هذه الأداة مثبتة افتراضيًا في أوبنتو 16.04، لكن تثبيتها سهلٌ جدًا في CentOS 7:
sudo yum install wget
ملاحظة: لتسهيل متابعة الأوامر الموجودة في هذه المقالة، سنُنزِّل ثلاث صور حرة من الإنترنت، أوّل صورتان منها (Junonia orithya و Mycalesis junonia) مرخصتان برخصة CC BY-SA 4.0 لصاحبها Jee & Rani Nature Photography؛ أما الصورة الأخيرة (Dental Care) فهي مرخصة برخصة CC0.
wget -c "https://upload.wikimedia.org/wikipedia/commons/2/24/Junonia_orithya-Thekkady-2016-12-03-001.jpg?download" -O /var/www/html/webp/image1.jpg
wget -c "https://upload.wikimedia.org/wikipedia/commons/5/54/Mycalesis_junonia-Thekkady.jpg" -O /var/www/html/webp/image2.jpg
wget -c "https://cdn.pixabay.com/photo/2017/07/18/15/39/dental-care-2516133_640.png" -O /var/www/html/webp/logo.png
معظم عملنا سيكون داخل المجلد /var/www/html/webp
، لذا سنغيّر مجلد العمل الحالي إليه بكتابة الأمر:
cd /var/www/html/webp
بعد أن ثبتنا خادم أباتشي، وفعّلنا إضافة mod_rewrite
، وثبتنا cwebp
وبعد أن ضبطنا مجلد التجارب، يمكننا الآن أن نتابع مع الخطوات القادمة.
الخطوة الثانية: ضغط الصور باستخدام cwebp
قبل أن نناقش كيفية تخديم صور .webp
إلى زوار موقعنا، علينا أن نُنشِئ نسخًا بهذه الصيغة من الصور الموجودة لدينا حاليًا. يمكننا استخدام الأداة cwebp
لتحويل صور JPEG أو PNG أو TIFF إلى صياغة .webp
. الصيغة العامة لهذا الأمر تبدو كما يلي:
cwebp image.jpg -o image.webp
يمكن تحديد مسار ملف المخرجات (بصيغة .webp
) بعد الخيار -o
، لاحظ أنَّ صور WebP تنتهي عادةً باللاحقة .webp
.
يمكننا ضبط جودة الصورة -q
إلى أيّ قيمة بين 0 و 100؛ وستكون القيمة الافتراضية، إن لم نضبطها، مساويةً إلى 75.
إذا نزّلتَ الصور التي ذكرناها في الخطوة الأولى، فيمكنك تنفيذ الأوامر الآتية لتحويل الصورة image1.jpg
إلى image1.webp
و image2.jpg
إلى image2.webp
. تذكّر أن تغيّر مجلد العمل الحالي إلى /var/www/html/webp
، ثم نفِّذ الأمر cwebp
لتحويل الصور بجودة 100%:
cd /var/www/html/webp
cwebp -q 100 image1.jpg -o image1.webp
cwebp -q 100 image2.jpg -o image2.webp
لنلقِ نظرةً على الحجم التخزيني لملفات JPEG و WebP باستخدام الأمر ls
. سنستخدم الخيار -l
لعرض الصيغة التفصيلية التي تتضمن حجم الملف التخزيني، والخيار -h
الذي يطلب من الأمر ls
أن يعرض الحجم بصيغةٍ سهلة القراءة:
ls -lh image1.jpg image1.webp image2.jpg image2.webp
سيُنتِج الأمر السابق الناتج الآتي:
-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
يُظهِر ناتج الأمر ls
أنَّ حجم الملف image1.jpg
هو 7.4 ميغابايت، بينما حجم الملف image1.webp
هو 3.9 ميغابايت؛ والمثل ينطبق على ملف image2.jpg
(16 ميغابايت) و image2.webp
(7.0 ميغابايت). لاحظ أنَّ حجم الملفات قد أصبح نصف ما كان عليه!
إذا كنّا نجري ضغطًا غير فقود (lossless، مما يعني أنَّ البيانات الأصلية للصورة ستُحفَظ كاملةً دون أيّ ضياع أثناء عملية الضغط)، فيمكننا أن نستعمل الخيار -lossless
، الذي يُعدّ أفضل طريقة للحفاظ على جودة صور PNG. إذا كانت لديك صور بصيغة PNG (كالتي نزّلناها في الخطوة السابقة) وتريد تحويلها إلى صيغة WebP لتقليل حجمها التخزيني، فيمكنك أن تستعمل الأمر الآتي:
cwebp -lossless logo.png -o logo.webp
الأمر الآتي يُظهِر أنَّ الحجم التخزيني لصورة WebP هو 60 كيلوبايت، وهو حوالي نصف الحجم التخزيني لصورة PNG (حوالي 116 كيلوبايت).
ls -lh logo.png logo.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
الخلاصة هي أنَّ الحجم التخزيني لنسخة WebP من الصور هي أقل بحوالي 50% من الحجم التخزيني لمثيلاتها من JPEG و PNG، لكن لاحظ أنَّ نسبة الضغط قد تختلف اعتمادًا على الصور التي عندك. يجدر بالذكر أنَّ هنالك عوامل أخرى كثيرة تؤثر على معدّل الضغط، بما في ذلك معدّل الضغط للصورة الأصلية، وصيغة الملف الأصلي، وطريقة التحويل (فقودة أم غير فقودة)، والجودة المطلوبة، ونظام التشغيل الذي تجري عليه عملية التحويل.
الخطوة الثالثة: تحويل جميع صور JPEG و PNG في أحد المجلدات
لقد حوّلنا بعض الملفات في الخطوة السابقة إلى صيغة WebP يدويًا، لكن فعل ذلك يأخذ وقتًا طويلًا ويتطلب جهدًا كثيرًا. يمكننا تبسيط هذه العملية بكتابة سكربت تحويل الذي سيبحث عن ملفات JPEG ويحولها إلى صيغة WebP بجودة 90%، وسيحوِّل أيضًا صور PNG إلى صور WebP غير فقودة.
أوّل عنصر في السكربت الذي سنكتبه هو الأمر find
. يمكنك تعلّم المزيد من المعلومات حول الأمر find
من الفصل السابع عشر «البحث عن الملفات» من كتاب «سطر أوامر لينكس». سيبحث الأمر find
عن الملفات في المجلد المُحدَّد، وسنستخدم معه بعض الخيارات لمطابقة أسماء تلك الملفات.
لنُنشِئ السكربت باستخدام المحرر النصي المُفضَّل لدينا. سنستخدم هاهنا المحرر nano
لكنك تستطيع أن تستعمل أيّ محرر ترتاح معه. سنُنشِئ الملف webp-convert.sh
في مجلد /var/www/html/webp
:
nano ~/webp-convert.sh
سيبدو أوّل سطر من الملف كما يلي:
find $1 -type f -and \( -iname "*.jpg" -o -iname "*.jpeg" \)
للحصول على مسار المجلد الذي يحتوي على الصور التي نريد تحويلها، فسنستخدم المعامل الموضعي $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. يجب أن يبدو الملف الذي نعمل عليه كما يلي في هذه المرحلة:
find $1 -type f -and \( -iname "*.jpg" -o -iname "*.jpeg" \) \
-exec bash -c 'commands' {} \;
أما داخل 'commands'
(أي السكربت الموجود بين علامتي اقتباس بعد الخيار -c
التابع للأمر bash
)، فسنبدأ بتهيئة متغير جديد باسم webp_path
للاحتفاظ بمسار ملف WebP الجديد، الذي سنولِّده بتبديل لاحقة الملف المُطابَق من الأمر find
(أي .jpg
أو .jpeg
) باللاحقة .webp
. سنستخدم الأمر sed
هنا لتبديل الملف لاحقة الملف الأصلي (أي جميع المحارف التي تأتي بعد آخر نقطة .
في اسم الملف) باللاحقة .webp
. رجاءً ارجع إلى قسم الأمر sed
في الفصل العشرين «معالجة النصوص» في كتاب سطر أوامر لينكس لمزيدٍ من المعلومات حوله. المعامل الموضعي $0
سيحمل مسار الملف المُطابَق هنا، وذلك لأنَّنا مررنا مسار كل ملف إلى الأمر bash
بوضع السلسلة النصية {}
كمعامل له (تذكّر أنَّ الشكل العامل للأمر كان bash -c 'commands' {} \;
). مرّرنا اسم الملف الناتج إلى مجرى الدخل القياسي للأمر sed
باستخدام here string وهي العلامة <<<
(راجع الصفحة 411 من كتاب سطر أوامر لينكس للتفاصيل):
webp_path=$(sed 's/\.[^.]*$/.webp/' <<< "$0");
للحفاظ على طاقة المعالجة في الخادم، فسنختبر إن كان هنالك ملفٌ محوّلٌ مسبقًا إلى صيغة WebP، وذلك باستخدام الأمر if
. سنختبر [
إن لم يكن !
هنالك ملفٌ -f
باسم "$webp_path"
موجودًا مسبقًا. لاحظ وجود علامتي اقتباس حول اسم الملف لتفادي حدوث مشاكل فيما لو احتوى على فراغات:
if [ ! -f "$webp_path" ]; then
# conversion command
fi;
في النهاية، إذا لم يكن الملف موجودًا، فسنستخدم الأمر cwebp
لإنشائه، كما فعلنا عند تحويل الصور في مجلد webp
في الخطوة السابقة:
cwebp -quiet -q 90 "$0" -o "$webp_path";
ملاحظة: ربما لاحظتَ أنَّ الأمر cwebp
يُظهِر الكثير من المخرجات، وقد لا نرغب بها في حال استخدمناه ضمن سكربتات، لذا يمكننا الاستفادة من الخيار -quiet
لإخبار الأمر cwebp
ألّا يعرض أيّ شيء.
يجب أن يبدو السكربت المستخدم لتحويل صور JPEG كما يلي عند هذه المرحلة:
# converting JPEG images
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;' {} \;
أما لتحويل صور PNG، فسنستعمل شيئًا شبيهًا جدًا بما فعلناه مع صور JPEG، باستثناء أمرين اثنين: أولهما هو تغيير النمط المستعمل في الأمر find
إلى "*.png"
، وثانيهما هو استعمال الخيار -lossless
في الأمر cwebp
لتحويل صور PNG بطريقة غير فقودة (كما ناقشنا في الخطوة الثانية).
سيبدو الملف كله كما يلي:
#!/bin/bash
# converting JPEG images
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;' {} \;
# converting PNG images
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;' {} \;
لنختبر الآن السكربت webp-convert.sh
، سنستخدم الملفات التي أنشأناها في الخطوة السابقة لتجربته. لكن احرص أولًا على جعل ملف السكربت قابلًا التنفيذ:
chmod a+x ~/webp-convert.sh
لنشغِّل السكربت على المجلد /var/www/html/webp
:
./webp-convert.sh /var/www/html/webp
لم يحدث شيء! ذلك لأنَّ السكربت بحث عن وجود نسخة .webp
من الصور في المجلد /var/www/html/wepb
، لكننا حولناها كلها في الخطوة الثانية من هذا الدرس. علينا إذًا أن نحصل على صورٍ جديدةٍ أو نحذف نسخة .webp
. الأمر الآتي سيحذف جميع ملفات .webp
الموجودة في المجلد /var/www/html/webp
:
rm /var/www/html/webp/*.webp
بعد حذف جميع صور .webp
، يمكننا تشغيل السكربت مرةً أخرى للتحقق أنَّه يعمل:
./webp-convert.sh /var/www/html/webp
سيؤكد الأمر ls
أنَّ السكربت قد حوّل الصور بنجاح:
ls -lh /var/www/html/webp
الناتج:
-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
هذه الخطوة هي أساس استخدام صور 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 في نواة نظام لينكس. يمكننا تثبيت هذه الحزمة في نظام أوبنتو بالأمر الآتي:
sudo apt-get install inotify-tools
أما لتوزيعة CentOS 7، فالحزمة inotify-tools
متوافرة في مستودع EPEL. يمكننا تثبيت مستودع EPEL وتثبيت الحزمة inotify-tools
باستخدام الأمرين الآتيين:
sudo yum install epel-release
sudo yum install inotify-tools
الأمر inotifywait
ينتظر حدوث تغييرات في مجلدٍ معيّن. الخيار -q
سيخبر الأمر inotifywait
ألّا يخرج الكثير من المخرجات. نرغب أيضًا أن يعمل الأمر inotifywait
دون أجلٍ مسمى (-m
) كيلا ينتهي تنفيذه بعد الحصول على أوّل حدث وقع في المجلد. إضافةً إلى ذلك، نرغب بضبط المراقبات في المجلد المُحدَّد وجميع المجلدات الفرعية التابعة له تعاوديًا باستخدام الخيار -r
.
الصيغة التي نريد الحصول عليها عند وقوع تغييرٍ في المجلد هي «اسم الحدث» متبوعًا بمسار الملف، ويمكننا تغيير الصيغة باستخدام الخيار --format
. الأحداث التي نريد مراقبتها هي close_write
(الذي سيُطلَق عند إنشاء ملف وانتهاء عملية كتابته إلى القرص)، و moved_from
و moved_to
(التي ستُطلَق عند نقل ملف)، والحدث delete
(الذي سيُطلَق عند حذف ملف).
يمكننا إنشاء السكربت بمحررنا النصي المُفضَّل كما فعلنا في الخطوة السابقة مع السكربت webp-convert.sh
. الأمر الآتي سيُنشِئ الملف webp-watchers.sh
في المجلد /var/www/html/webp
:
nano ~/webp-convert.sh
سيبدو أوّل سطرٍ في السكربت كما يلي:
inotifywait -q -m -r --format '%e %w%f' -e close_write -e moved_from -e moved_to -e delete $1
لتفحص ما هي المخرجات التي سيُظهرها هذا الأمر، فنحاول تشغيله في المجلد /var/www/html/webp
، مع تشغيله في الخلفية بإضافة الرمز &
في نهاية الأمر. سننتقل الآن من تعديل السكربت إلى سطر الأوامر لاختبار سلوك الأمر inotifywait
(احرص على حفظ الملف والخروج من المحرِّر):
inotifywait -q -m -r --format '%e %w%f' -e close_write -e moved_from -e moved_to -e delete /var/www/html/webp &
لمعرفة المخرجات التي يُنتِجها الأمر inotifywait
، فسنُنشِئ عدِّة ملفات ونجري عمليات عليها. سنُنِشئ أولًا ملفًا جديدًا فارغًا باسم new_file
باستخدام الأمر touch
. أدخِل الأمر الآتي في سطر الأوامر:
touch /var/www/html/webp/new_file
ثم سنُعيد تسمية الملف (أي ننقله) إلى moved_file
باستخدام الأمر mv
:
mv /var/www/html/webp/new_file /var/www/html/webp/moved_file
وفي النهاية سنحذف الملف moved_file
باستخدام الأمر rm
:
rm /var/www/html/webp/moved_file
سنشاهد ناتجًا شبيهًا بالناتج الآتي في الطرفية:
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
سيظهر أوّل سطر بعد إنشاء الملف الجديد، وسيظهر السطران الثاني والثالث بعد نقل الملف (ويشيران إلى مسار المصدر والوجهة). في النهاية، آخر سطر يُظهِر أنَّ الملف قد حُذِف.
بالعودة إلى أوّل سطر في سكربت webp-watchers.sh
، سنستخدم الأمر grep
لمعرفة إن كانت الملفات التي أطلقت أحد الأحداث السابقة هي ملفات بصيغة JPEG أو PNG؛ ولمّا كان الأمر inotifywait
لا يوفِّر الخيار --include
لمراقبة الملفات التي تُطابِق تعبيرًا نمطيًا محدَّدًا، فنحن مجبرون لاستخدام الأمر grep
لحلّ التفافي لهذا القصور. أمر grep
الآتي سيؤدي إلى مطابقة صور JPEG أو PNG فقط. وسنستخدم فيه الخيار -i
لتجاهل حالة الأحرف، والخيار -E
لاستخدام التعابير النمطية الموسّعة (extended regular expressions). سنضيف أيضًا الخيار --line-buffered
إلى أمر grep
لتمرير الأسطر التي جرت مطابقتها إلى حلقة التكرار (التي سنُناقشها بعد قليل):
| grep -i -E '\.(jpe?g|png)$' --line-buffered
أي أنَّ ناتج الأمر inotifywait
سيُمرِّر مسار الملفات التي تغيّرت في المجلد المُحدَّد عبر أنبوب (pipe، أي الرمز |
) إلى الأمر grep
، الذي بدوره سيتحقق إن كان الملف صورةً.
الخطوة الآتية هي بناء حلقة التكرار while
. يمكننا الاستفادة من حلقة التكرار while
مع الأمر read
في حالات مثل حالتنا؛ مما يسمح لنا بمعالجة كل سطر من الأسطر المُمرَّرة إلى الحلقة فور إخراجها. سيُخزِّن الأمر read
الحدث في متغيرٍ باسم $operation
وسيخزِّن مسار الملف المُعالَج في متغيرٍ باسم $path
:
| while read operation path; do
# commands
done;
لندمج ذلك مع أمر inotifywait
الموجود في أوّل سطر من السكربت:
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
# commands
done;
حلقة while
ستتحقق من الحدث المُطلَق من الأمر inotifywait
، ومن ثم ستجري الأوامر الموجودة داخل الحلقة الأفعال الآتية:
إنشاء ملف WebP جديد إن أنشأنا صورةً جديدةً أو نقلناها إلى المجلد الهدف.
حذف ملف WebP إذا حُذِفَت الصورة الأصلية أو نُقِلَت من المجلد الهدف.
هنالك ثلاثة أقسام رئيسية في حلقة التكرار. أولها هو تعريف متغيرٍ باسم webp_path
الذي هيّأناه ليُخزِّن نسخة .webp
من الصورة الهدف (أنشأناه بأسلوب مشابه لما فعلناه في الخطوة الثالثة):
webp_path="$(sed 's/\.[^.]*$/.webp/' <<< "$path")";
الخطوة المنطقية التالية هي اختبار الحدث الذي وقع:
if [ $operation = "MOVED_FROM" ] || [ $operation = "DELETE" ]; then
# commands to be executed if the file is moved or deleted
elif [ $operation = "CLOSE_WRITE,CLOSE" ] || [ $operation = "MOVED_TO" ]; then
# commands to be executed if a new file is created
fi;
إذا نقلنا أو حذفنا الملف، فسنتحقق من وجود نسخة .webp
منه، فإذا كانت موجودةً فسنحذفها باستخدام الأمر rm
:
if [ -f "$webp_path" ]; then
$(rm -f "$webp_path");
fi;
أما بالنسبة إلى الملفات المُنشَأة حديثًا، فسنختبر إن كان الملف بصيغة PNG (باستخدام if
و grep
كما فعلنا في الخطوة الثالثة). فعلنا لذا سيسمح لنا باستخدام التحويل غير الفقود (عبر الخيار -lossless
). إذا لم يكن الملف المُطابَق بصيغة PNG فسنجري تحويلًا فقودًا باستخدام نفس الأمر المذكور في الخطوة الثانية (أي بتحديد جودة الصورة عبر المعامل -q
):
if [ $(grep -i '\.png$' <<< "$path") ]; then
$(cwebp -quiet -lossless "$path" -o "$webp_path");
else
$(cwebp -quiet -q 90 "$path" -o "$webp_path");
fi;
قد تتساءل لماذا لم ننقل ملف .webp
عند نقل الملف الأصلي المرتبط به، ذلك لأننا اتبعنا المنهجية الأسهل: حذف ملف WebP ثم إعادة إنشاءه من الصفر؛ ذلك لأننا لا نريد تعقيد السكربت في حال أضفنا الجزء المسؤول عن نقل نسخة .webp
المرتبطة بالملفات الأصلية.
يجب أن يكون كامل ملف webp-watchers.sh
كما يلي:
#!/bin/bash
echo "Setting up watches.";
# watch for any created, moved, or deleted image files
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
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;
لا تنسَ أن تجعل السكربت قابلًا للتنفيذ:
chmod a+x ~/webp-convert.sh
يمكننا الآن تشغيل السكربت على المجلد /var/www/html/webp
لمراقبته (وجميع المجلدات الفرعية فيه) للتغيرات. ربما ترغب بتشغيل الأمر في الخلفية بإضافة الرمز &
في نهاية الأمر:
./webp-watchers.sh /var/www/html/webp
في هذه المرحلة، حوّلنا جميع الصور الموجودة في المجلد /var/www/html/webp
إلى صيغة WebP، وضبطنا المراقبات باستعمال سكربت webp-watchers.sh
. حان الوقت الآن لاستكشاف خياراتنا بخصوص طريقة تخديم صور WebP إلى زوار مواقعنا.
الخطوة الخامسة: تخديم ملفات WebP إلى الزوار
إذا اتبعتَ التعليمات الموجودة في الخطوات السابقة، فيجب أن تصبح عندك نسخة .webp
لكل صورة JPEG أو PNG موجودة في مجلد /var/www/html/webp
. يمكننا الآن التفكير بطريقة تخديم تلك الصور إلى المتصفحات الداعمة لها.
سنفعل ذلك بمنهجيتين اثنتين: استخدام عناصر HTML5 (العنصر <picture>
تحديدًا)، ووحدة mod_rewrite
في خادم الويب أباتشي. سنناقش طريقة استخدام العنصر <picture>
في هذه الخطوة، وسنُرجئ شرح الوحدة mod_rewrite
إلى الخطوة القادمة.
إذا كنتَ مطوِّر (أو صائن) موقعك، فيمكنك استخدام العنصر <picture>
لتضمين الصور في صفحات الويب في موقعك. يسمح هذا العنصر بتعريف أكثر من مصدر (source) للصورة. وإذا كان المتصفح يدعم صيغة WebP فسيُنزِّل نسخة .webp
بدلًا من الصورة الأصلية، مما يُسرِّع تخديم الصفحة. يجدر بالذكر أنَّ العنصر <picture>
مدعومٌ دعمًا جيدًا في المتصفحات الحديثة التي تدعم صيغة WebP.
العنصر <picture>
هو حاوية التي تحتوي على عناصر <source>
لتحديد «مصدر» مختلف للصورة المُحدَّدة في العنصر <img>
المحتوى فيها. إذا استخدمنا العنصر <source>
للإشارة إلى صورة .webp
، فسينظر المتصفح إن كان قادرًا على تفسير تلك الصفحة، وإن لم يكن قادرًا فسيستعمل الصورة المُحدَّدة في الخاصية src في العنصر <img>
.
لنقل أنَّ لدينا صورة PNG باسم logo.png
وحولنا إلى logo.webp
باستخدام السكربتات السابقة، فيمكننا استخدام شيفرة HTML الآتية لعرض الصورة logo.webp
لأي متصفح يدعم صيغة WebP، والصورة logo.png
للمتصفحات الأخرى التي لا تدعم صيغة WebP أو لا تدعم العنصر <picture>
. لنفتح محررنا النصي المُفضَّل ونُنشِئ ملف HTML في المسار /var/www/html/webp/picture.html
:
nano /var/www/html/webp/picture.html
سنختصر الصفحة الاختبارية إلى البنية الآتية:
<picture>
<source srcset="logo.webp" type="image/webp">
<img src="logo.png" alt="Site Logo">
</picture>
يمكن استخدام العنصر <picture>
لتوفير أكثر من نسخة من الصورة، مما يسمح للمتصفح باختيار أكثر صورة ملائمة بينها لعرضها. هذا أبسط خيارٍ متاحٍ أمامنا لتخديم صور .webp
.
بعد أن شرحنا كيفية تخديم صور .webp
مباشرةً من شيفرة HTML، سننتقل الآن إلى شرح طريقة أتمتة هذه العملية باستخدام وحدة mod_rewrite
التابعة لخادم أباتشي.
الخطوة السادسة: تخديم صور WebP باستخدام وحدة mod_rewrite
إذا أردنا تحسين سرعة موقعنا، لكنه يحتوي على عددٍ كبيرٍ من الصفحات أو لدينا وقتٌ وموارد قليلة لتعديل شيفرة HTML، فستساعدنا وحدة mod_rewrite
في أتمتة عملية تخديم صور .webp
إلى المتصفحات الداعمة لها.
أولًا، علينا اختبار إذا كانت وحدة mod_rewrite
متوافرةً، وذلك في ملف .htaccess
باستخدام التعليمة ifModule
؛ وإذا كانت الوحدة متاحةً فسنُفعِّلها باستخدام التعليمة RewriteEngine On
. يمكن إنشاء ملف .htaccess
في مجلد /var/www/html/webp
باستخدام الأمر الآتي:
nano /var/www/html/webp/.htaccess
الصيغة الأوليّة لملف .htaccess
ستكون كما يلي:
<ifModule mod_rewrite.c>
RewriteEngine On
# further directives
</IfModule>
لكي نعرف متى علينا تخديم صور .webp
إلى المستخدم، فيجب أن يجري خادم الويب عدِّة اختبارات. فعندما يجري المتصفح طلبيةً، فسيُضمِّن فيها ترويسةً تُخبِر الخادم ماذا يستطيع المتصفح تشغيله. وفي حالة صور WebP، سيُرسِل الخادم ترويسة Accept
التي تحتوي على image/webp
. سنتحقق إذا كان المتصفح قد أرسل هذا الترويسة باستخدام التعليمة RewriteCond
. التعليمة RewriteCond
تُحدِّد المعايير أو الشروط التي يجب أن تُطابَق حتى يُنفَّذ ما في التعليمة RewriteRule
. راجع توثيق وحدة mod_rewrite
الرسمي لمزيدٍ من المعلومات:
RewriteCond %{HTTP_ACCEPT} image/webp
علينا ترشيح كل الملفات ما عدا صور JPEG و PNG، وذلك باستخدام تعبيرٍ نمطيٍ شبيهٍ بالتعبير المستخدم في الأقسام السابقة، وذلك لمطابقة عنوان URI المطلوب من المتصفح. لاحظ أننا استخدمنا (?i)
لجعل عملية المطابقة غير حساسة لحالة الأحرف:
RewriteCond %{REQUEST_URI} (?i)(.*)(\.jpe?g|\.png)$
لتتحقق أنَّ نسخة .webp
من الملف موجودة (من البدهي أننا لا نريد تخديم صورة غير موجودة إلى المستخدم)، فيمكننا استخدام تعليمة RewriteCond
كما يلي:
RewriteCond %{DOCUMENT_ROOT}%1.webp -f
في النهاية، إذا تحققت كل الشروط السابقة، فسنُعيد توجيه صورة JPEG أو PNG المطلوبة إلى ملف WebP المرتبط بها. لاحظ أننا «نعيد توجيه الملف» (redirect، أي سيُطلَب من المتصفح أن يجري طلبية جديدة لرابط URI جديد يُشير إلى ملف .webp
) باستخدام الراية R
، ولا «نعيد كتابة رابط URI» (rewrite). الفرق بين إعادة التوجيه وإعادة كتابة رابط URI هي أنَّ المتصفح سيُخدِّم رابط URI الجديد دون إخبار المتصفح بذلك، مما يعني أنَّ الملف المنقول إلى المتصفح هو .webp
لكنّه سيبقى مخدّمًا بنفس رابط URI الأصلي. بعبارةٍ أخرى، سيظهر أنَّ رابط URI يُشير إلى ملف .png
(على سبيل المثال)، لكنه في الحقيقة ملف .webp
:
RewriteRule (?i)(.*)(\.jpe?g|\.png)$ %1\.webp [L,T=image/webp,R]
عند هذه المرحلة، أنهينا قسم mod_rewrite
في ملف .htaccess
، لكن ماذا سيحدث لو كان هنالك خادم تخزين مؤقت بين خادمنا وجهاز المستخدم؟ قد يؤدي ذلك إلى تخديم النسخة الخطأ من الملف إلى الزائر، ولهذا السبب علينا أن نتحقق إذا كانت الوحدة mod_headers
مفعّلةً، وذلك لإرسال ترويسة Vary: Accept
. الترويسة Vary
تُخبر الخوادم الوسيطة (proxy servers) أنَّ نوع المحتوى للمستند (أي MIME type. بعبارةٍ أخرى: صيغة الملف المُخدَّم) تختلف (Vary
) اعتمادًا على قدرات المتصفح الذي طلب الملف. إضافةً إلى ذلك، إنَّ المحتوى المُخدَّم إلى الزائر قد وُلِّد اعتمادًا على ترويسة Accept
الموجودة في الطلبية. فطلبيةٌ أخرى لها ترويسة Accept
مختلفة قد تحصل على ناتج آخر. لاحظ أنَّ إرسال هذه الترويسة هو أمرٌ ضروري لمنع الخوادم الوسيطة من تخديم صور WebP إلى المتصفحات غير الداعمة لها:
<IfModule mod_headers.c>
Header append Vary Accept env=REDIRECT_accept
</IfModule>
في نهاية ملف .htaccess
، سنضبط نوع MIME لملفات .webp
إلى image/webp
باستخدام التعليمة AddType
. فعلنا ذلك لتخديم الصور باستخدام نوع MIME الصحيح:
AddType image/webp .webp
هذه هي النسخة النهائية من ملف .htaccess
:
<ifModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond %{REQUEST_URI} (?i)(.*)(\.jpe?g|\.png)$
RewriteCond %{DOCUMENT_ROOT}%1.webp -f
RewriteRule (?i)(.*)(\.jpe?g|\.png)$ %1\.webp [L,T=image/webp,R]
</IfModule>
<IfModule mod_headers.c>
Header append Vary Accept env=REDIRECT_accept
</IfModule>
AddType image/webp .webp
ملاحظة: لاحظ أنَّك تستطيع دمج ملف .htaccess
السابقة مع الملف الموجود مسبقًا عندك. فلو كنتَ تستخدم ووردبريس مثلًا، فيمكنك نسخ ملف .htaccess
السابق ولصقه في بداية الملف الموجود عندك.
لنجرِّب ما فعلناه في هذه الخطوة. لو اتبعنا الخطوات السابقة، فيجب أن يكون لدينا ملف باسم logo.png
و logo.webp
في مجلد /var/www/html/webp
. لنجرِّب استخدام عنصر <img>
بسيط لتضمين صورة الشعار logo.png
في صفحة الويب. يمكننا إنشاء ملف HTML تجريبي مثلما فعلنا في الخطوة السابقة:
nano /var/www/html/webp/img.html
أدخِل شيفرة HTML في ملف /var/www/html/webp/img.html
:
<img src="logo.png" alt="Site Logo">
عندما تزو صفحة الويب السابقة باستخدام متصفح Chrome بزيارة الرابط http://example.com/webp/img.html فستلاحظ أنَّ الصورة المُخدَّمة هي نسخة .webp
من الشعار (جرِّب مثلًا فتح الصورة في لسانٍ جديد)؛ أما إذا كنتَ تستخدم Firefox فستحصل على نسخة .png
تلقائيًا.
الخلاصة
تعلمنا في هذه المقالة عن كيفية استخدام صيغة صور 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 والتي كتبها عبد اللطيف ايمش.