مرحبا يا هبر! أقدم إليكم ترجمة المقالة
"5 أسباب يجب عليك التوقف عن استخدام System.Drawing من ASP.NET" .

حسنا ، لقد فعلوا ذلك. وافق فريق corefx في النهاية على
العديد من الطلبات وضمّن System.Drawing في .NET Core.
(المقال الأصلي بتاريخ يوليو 2017)
ستحتوي الحزمة
System.Drawing.Common القادمة على معظم وظائف System.Drawing من .NET Framework الكامل وتهدف إلى استخدامها كخيار توافق لأولئك الذين يرغبون في الانتقال إلى .NET Core ولكن لا يمكنهم القيام بذلك بسبب التبعيات. من هذا المنظور ، تقوم Microsoft بالشيء الصحيح. يجب تقليل الاحتكاك لأن اعتماد .Net Core يعد هدفًا أكثر أهمية.
من ناحية أخرى ، يعد System.Drawing واحدًا من أكثر المناطق فقراً وحرمانًا من .Net Framework وكثير منا يأمل أن يؤدي تطبيق .NET Core إلى الموت البطيء لـ System.Drawing. وجنبا إلى جنب مع هذا الموت يجب أن تكون فرصة لفعل شيء أفضل.
على سبيل المثال ، قام فريق Mono بصنع غلاف متوافق مع .NET لمكتبة رسومات
Skia عبر النظام الأساسي لـ Google ، والتي تسمى
SkiaSharp . لجعل عملية التثبيت بسيطة ، قطعت Nuget شوطًا كبيرًا في دعم المكتبات الأصلية لكل نظام أساسي. إن Skia ممتلئ تمامًا وأدائه يقوم System.Drawing.
قام فريق
ImageSharp أيضًا بعمل رائع في تكرار الكثير من وظائف System.Drawing ، ولكن مع أفضل واجهات برمجة التطبيقات وتطبيق 100٪ C #. لا يزالون غير مستعدين للاستغلال المثمر ، لكن يبدو أنهم قريبون بالفعل من ذلك. تحذير صغير حول هذه المكتبة ، نظرًا لأننا نتحدث عن الاستخدام في تطبيقات الخادم: الآن ، في التكوين الافتراضي ، يتم استخدام Parallel.For في الداخل لتسريع بعض العمليات ، مما يعني أنه سيتم استخدام المزيد من مهام سير العمل من تجمع ASP.NET ، في النهاية نتيجة لذلك ،
تقليل الإنتاجية الإجمالية للتطبيق . آمل أن تتم مراجعة هذا السلوك قبل الإصدار ، ولكن حتى الآن يكفي تغيير سطر واحد من التكوين لجعله أكثر ملاءمة للاستخدام على الخادم.
في أي حال ، إذا كنت تقوم برسم أو تخطيط أو تقديم نص إلى صور في تطبيق على الخادم ، فيجب أن تفكر بجدية في تغيير System.Drawing إلى أي شيء ، بغض النظر عما إذا كنت تقوم بالتبديل إلى .NET Core أم لا.
من جهتي ، قمت بتجميع خط أنابيب معالجة صور عالي الأداء لـ .NET و .NET Core ، يوفر جودة صورة لا يمكن لـ System.Drawing توفيرها ، ويقوم بذلك في بنية قابلة للتطوير بشكل كبير مصممة خصيصًا للاستخدام على الخادم. حتى الآن ، لا يقتصر الأمر على نظام Windows ، إلا أن النظام الأساسي المتقاطع في الخطط. إذا كنت تستخدم System.Drawing (أو أي شيء آخر) لتغيير حجم الصور على الخادم ، فمن الأفضل اعتبار
MagicScaler بديلاً.
ولكن من المحتمل أن تؤدي عملية القيامة التي يقوم بها System.Drawing ، والتي تسهل عملية الانتقال لبعض المطورين ، إلى قتل معظم الزخم الذي تلقته هذه المشروعات ، حيث اضطر المطورون للبحث عن بدائل. لسوء الحظ في النظام البيئي .NET ، ستفوز مكتبات Microsoft وحزمها دائمًا ، بغض النظر عن مدى تفوق البدائل.
هذا المنشور هو محاولة لإصلاح بعض الأخطاء في System.Drawing على أمل أن يستكشف المطورون البدائل حتى لو ظل System.Drawing خيارًا.
سأبدأ
بإخلاء المسؤولية الذي يتم الاستشهاد به في كثير من الأحيان
من وثائق System.Drawing. لقد تم رفع هذا الرفض عدة مرات في
مناقشة حول Github عند مناقشة System.Drawing.Common .
"الفئات مع مساحة الاسم System.Drawing غير مدعومة للاستخدام في خدمات Windows أو ASP.NET. محاولة استخدام هذه الفئات مع هذه الأنواع من التطبيقات يمكن أن تسبب مشاكل غير متوقعة ، مثل انخفاض أداء الخادم وأخطاء وقت التشغيل. "
مثل العديد منكم ، قرأت إخلاء المسؤولية هذا منذ وقت طويل جدًا ، وبعد ذلك تخطيت ذلك وكنت لا أزال أستعمل System.Drawing في تطبيق ASP.NET الخاص بي. لماذا؟ لأنني أحب الخطر. إما ذلك ، أو لم يتم العثور على خيارات قابلة للحياة. وانت تعرف ماذا؟ لم يحدث شيء سيء. على الأرجح لا ينبغي أن أقول هذا ، لكنني أراهن أن الكثير منكم قد اختبر نفس الشيء. فلماذا لا تستمر في استخدام System.Drawing أو المكتبات بناءً عليه؟
السبب رقم 1: واصفات GDI
إذا كنت تواجه مشكلات في استخدام System.Drawing على خادم ، فهذا هو الحال على الأرجح. إذا لم يتم اختباره ، فهذا أحد الأسباب المحتملة.
System.Drawing بالنسبة للجزء الأكبر هو غلاف رقيق حول Windows GDI + API. يتم دعم العديد من كائنات System.Drawing بواسطة
واصفات GDI ، ولديها حد كمي على جلسة عمل المعالج والمستخدم. إذا تم الوصول إلى هذه العتبة ، فستحصل على استثناء "نفاد الذاكرة" و / أو خطأ GDI + "عام".
المشكلة هي أنه في .NET ، يمكن أن يؤدي تجميع البيانات المهملة وإنهاء العملية إلى تأخير إصدار هذه الواصفات بحلول الوقت الذي تصل فيه إلى الحد الأقصى ، حتى في ظل التحميل الخفيف. إذا نسيت (أو لم تكن تعرف ما تحتاجه) للاتصال Dispos () على كائن يحتوي على مثل هذه الواصفات ، فأنت في خطر كبير لمواجهة مثل هذه الأخطاء في بيئتك. ومثل معظم الأخطاء المتعلقة بالقيود على الموارد أو التسريبات ، فعلى الأرجح سيتم اختبار هذا الموقف بنجاح ويخنقك في عملية منتجة. بطبيعة الحال ، سوف يحدث هذا عندما يكون التطبيق الخاص بك تحت العبء الأكبر ، بحيث يكتشف الحد الأقصى لعدد المستخدمين عن عارك.
تعتمد القيود المفروضة على المعالج وجلسة المستخدم على إصدار نظام التشغيل ، ويمكن تخصيص القيود المفروضة على المعالج. لكن الإصدار لا يهم ، لأن يتم تمثيل واصفات GDI داخليًا بواسطة نوع بيانات USHORT ، لذلك يوجد حد صارم يبلغ 65536 واصفًا لكل جلسة مستخدم ، وحتى مخاطر تطبيق مكتوبة جيدًا تصل إلى هذا الحد في ظل تحميل كافٍ. عندما تعتقد أن خادمًا أكثر قوة سيتيح لك خدمة المزيد والمزيد من المستخدمين بالتوازي في حالة واحدة ، يصبح هذا الخطر أكثر واقعية. وحقا ، من يريد إنشاء برنامج مع حد ثابت معروف من قابلية التوسع؟
السبب رقم 2: التزامن
واجهت GDI + دائمًا مشاكل في التزامن ، على الرغم من أن العديد منها كان مرتبطًا
بالتغييرات المعمارية في Windows7 / Windows Server 2008 R2 ، لا تزال ترى بعضها في الإصدارات الجديدة. الأكثر بروزًا هو
تأمين العملية الذي تم ترتيبه بواسطة GDI + أثناء عملية DrawImage (). إذا قمت بتغيير حجم الصور على الخادم باستخدام System.Drawing (أو المكتبات التي التفافها) ، فربما تكون طريقة DrawImage () هي أساس هذا الرمز.
علاوة على ذلك ، عند إجراء عدة مكالمات إلى DrawImage () في نفس الوقت ، سيتم حظرها
جميعًا حتى يتم تنفيذها جميعًا. حتى إذا لم يكن وقت الاستجابة يمثل مشكلة بالنسبة لك (لماذا لا؟ هل تكره المستخدمين لديك؟) ضع في اعتبارك أن أي موارد ذاكرة مرتبطة بهذه الطلبات وجميع واصفات GDI التي تحتفظ بها الكائنات المرتبطة بهذه الطلبات مرتبطة بوقت التشغيل. في الواقع ، لا يتطلب الأمر تحميل الكثير على الخادم لبدء التسبب في حدوث مشكلات.
بالطبع ، هناك حلول لهذه المشكلة بالذات. على سبيل المثال ، يقوم بعض المطورين بإنشاء عملية خارجية لكل عملية DrawImage (). ولكن في الواقع ، فإن مثل هذا الحل يضيف فقط هشاشة إضافية ، وهو ما لا ينبغي عليك فعله.
السبب رقم 3: الذاكرة
خذ بعين الاعتبار معالج ASP.NET الذي ينشئ مخطط. يجب عليه فعل شيء مثل هذا:
- إنشاء صورة نقطية كقماش
- ارسم أشكالًا متعددة على صورة نقطية باستخدام الأقلام و / أو الفرش
- ارسم النص باستخدام خط واحد أو أكثر
- احفظ الصورة النقطية بتنسيق PNG إلى MemoryStream
دعنا نقول الرسم البياني يقيس 600 في 400 نقطة. هذا هو إجمالي 240،000 نقطة ، مضروبة في 4 بايت لنقطة لتنسيق RGBA الافتراضي ، ويبلغ إجماليها 960،000 بايت لصورة نقطية ، بالإضافة إلى القليل للكائنات الرسومية ومخزن مؤقت للحفظ. فليكن 1 ميغابايت للطلب بأكمله. على الأرجح أنك لن تواجه مشكلات في الذاكرة في مثل هذا السيناريو ، وإذا واجهت أي شيء ، فمن المرجح أن يكون لديك حد لعدد الواصفات ، التي ذكرتها سابقًا ، لأن الصور والفرش والأقلام والخطوط لها واصفات خاصة بها.
المشكلة الحقيقية تأتي عند استخدام System.Drawing لمهام التصوير. System.Drawing هو مكتبة رسومات بشكل أساسي ، وعادة ما يتم بناء مكتبات الرسومات حول فكرة أن كل شيء عبارة عن صورة نقطية في الذاكرة. هذا شيء رائع بينما تفكر في الأشياء الصغيرة. لكن يمكن أن تكون الصور كبيرة جدًا ، وتزداد حجمها يوميًا ، لأن كاميرات مع الكثير من ميغابكسل هي الحصول على أرخص باستمرار.
إذا اتبعت منهج System.Drawing الساذج في بناء الصور ، فستحصل على شيء مثل هذا لمعالج تغيير الحجم:
- إنشاء صورة نقطية كقماش للصورة الوجهة.
- قم بتحميل الصورة الأصلية في صورة نقطية أخرى.
- استدعاء DrawImage () مع المعلمة "مصدر الصورة" للصورة الوجهة ، باستخدام تغيير حجم.
- احفظ الصورة النقطية الهدف بتنسيق JPEG في دفق الذاكرة.
افترض أن الصورة الهدف ستكون 600 × 400 ، كما في المثال السابق ، ثم مرة أخرى لدينا 1 ميغابايت
للصورة الهدف ودفق الذاكرة. لكن لنفترض أن شخصًا قام بتحميل صورة بحجم 24 ميجابكسل من DSLRs الجديدة الفاخرة ، فإننا نحتاج إلى 6000 × 4000 بكسل مع 3 بايت لكل (72 ميجابايت) للصورة النقطية للمصدر التي تم فك تشفيرها بتنسيق RGB. وسوف نستخدم إعادة التشكيل عالية الجودة من System.Drawing ، لأنها تبدو جيدة فقط. ثم نحتاج إلى أن نأخذ في الاعتبار نقاط 6000 × 4000 الأخرى مع 4 بايت لكل منها ،
لتحويل PRGBA الذي يحدث داخل الطريقة التي تم استدعاؤها ، مع إضافة 96 ميغابايت إضافية من الذاكرة المستخدمة. في المجموع ، تم الحصول على 169mb (!) لطلب تحويل صورة واحدة.
الآن تخيل أن لديك أكثر من مستخدم يقوم بهذه الأشياء. تذكر الآن أنه تم حظر الطلبات حتى يتم تنفيذها بالكامل. كم من الوقت يستغرق نفاد الذاكرة؟ وحتى إذا كنت لا تقلق من استنفاد كل ما هو متاح تمامًا ، تذكر أن هناك العديد من الطرق لاستخدام ذاكرة الخادم بشكل أفضل من الاحتفاظ بمجموعة من البكسلات. النظر في تأثير ضغط الذاكرة على أجزاء أخرى من التطبيق / النظام:
- قد تبدأ ذاكرة التخزين المؤقت ASP.NET في تدفق العناصر المكلفة لإعادة
- سيبدأ جامع البيانات في كثير من الأحيان ، مما يؤدي إلى إبطاء التطبيق
- يمكن لـ kernel IIS أو ذاكرة التخزين المؤقت لنظام ملفات Windows إزالة العناصر المفيدة
- تجمع التطبيقات قد يتجاوز حد الذاكرة ويمكن إعادة تشغيله
- قد يبدأ Windows في تبديل الذاكرة إلى القرص ، مما يؤدي إلى إبطاء النظام بأكمله
أنت حقا لا تريد أي شيء من هذا؟
تعالج المكتبات المصممة خصيصًا لمهام معالجة الصور هذه المشكلة بطريقة مختلفة تمامًا. لا يحتاجون إلى تحميل المصدر أو الصورة المستهدفة بأكملها في الذاكرة. إذا كنت لن ترسم عليها ، فلن تحتاج إلى صورة قماشية / صورة نقطية. يتم ذلك أكثر مثل هذا:
- إنشاء تيار لتشفير JPEG للصورة الهدف
- قم بتحميل سطر واحد من الصورة الأصلية وضغطه أفقياً
- كرر عدة مرات حسب الحاجة لتكوين سطر واحد للملف الهدف
- ضغط الخطوط الناتجة عموديا
- كرر من الخطوة 2 حتى تتم معالجة جميع أسطر الملف المصدر.
باستخدام هذه الطريقة ، يمكن معالجة نفس الصورة باستخدام 1 ميغابايت من الذاكرة في المجموع ، وسوف تتطلب الصور الأكبر حجمًا زيادة طفيفة في الحمل.
أعرف مكتبة .NET واحدة فقط تم تحسينها بواسطة هذا المبدأ ، وسأعطيك تلميحًا: هذا ليس System.Drawing.
السبب رقم 4: وحدة المعالجة المركزية
تأثير جانبي آخر لـ System.Drawing كونه أكثر توجهاً بيانياً من أن يكون موجهاً نحو الصورة هو أن DrawImage () غير فعال إلى حد كبير من حيث استخدام وحدة المعالجة المركزية. لقد غطيت ذلك بشيء من التفصيل في منشور
سابق ، ولكن يمكن تلخيص هذه المناقشة بالحقائق التالية:
- في System.Drawing ، يعمل تحويل المقياس HighQualityBicubic فقط مع تنسيق PRGBA. في جميع السيناريوهات تقريبًا ، يعني هذا نسخة إضافية من الصورة. هذا لا يستخدم فقط (بشكل كبير) ذاكرة إضافية ، بل يحرق دورات المعالج لتحويل ومعالجة قناة ألفا الإضافية.
- حتى بعد أن تكون الصورة بتنسيقها الأصلي ، فإن تحويل المقياس HighQualityBicubic ينفذ حوالي 4 مرات حسابات أكثر مما هو ضروري للحصول على نتائج التحويل الصحيحة.
تضيف هذه الحقائق كمية كبيرة من دورات وحدة المعالجة المركزية الضائعة. في بيئة غائمة مع الدفع في الدقيقة ، يساهم هذا بشكل مباشر في تكلفة الاستضافة. وبطبيعة الحال ، سوف وقت الاستجابة الخاصة بك تعاني.
وفكر في حقيقة أنه سيتم إنفاق كهرباء إضافية وتوليد الحرارة. استخدامك لـ System.Drawing لمهام معالجة الصور يؤثر بشكل مباشر على ظاهرة الاحتباس الحراري. انت وحش.
السبب رقم 5: معالجة الصور معقدة بشكل خادع
الأداء جانباً ، يمنع System.Drawing بعدة طرق معالجة الصورة بشكل صحيح. استخدام System.Drawing يعني إما التعايش مع مخرجات غير صحيحة ، أو تعلم كل شيء عن ملف تعريف ICC ، وتقدير اللون ، واتجاه exif ، والتصحيح ، والعديد من الأشياء الأخرى المحددة. هذا ثقب أرنب ، لا يتمتع معظم المطورين بالوقت ولا الرغبة في استكشافه.
اكتسبت المكتبات ، مثل ImageResizer و ImageProcessor ، الكثير من المعجبين ، حيث تهتم ببعض هذه التفاصيل ، ولكن كن حذراً ، لديهم System.Drawing في الداخل ، ويأتي مع جميع الأمتعة التي وصفتها بالتفصيل في هذه المقالة.
مكافأة السبب: يمكنك أن تفعل أفضل
إذا كان عليك مثلي ارتداء النظارات في مرحلة ما من حياتك ، فربما تتذكر كيف كانت المرة الأولى التي ترتدي فيها هذه النظارات. اعتقدت أنني كنت أراه بشكل طبيعي ، وإذا كنت أحول بشكل صحيح ، فسيكون كل شيء واضحًا. لكنني أرتدي هذه النظارات ، وأصبح العالم أكثر تفصيلاً مما كنت أتخيل.
System.Drawing هو نفسه تقريبا. يعمل هذا بشكل صحيح إذا قمت
بملء الإعدادات بشكل صحيح ، ولكن ستندهش من مدى إمكانية ظهور صورك إذا كنت تستخدم أفضل الأدوات المساعدة.
سأترك هذا هنا كمثال. هذا هو أفضل ما يمكن لـ System.Drawing القيام به مقارنة بإعدادات MagicScaler الافتراضية. ربما سيستفيد التطبيق الخاص بك من الحصول على نقاط ...
GDI:

MagicScaler:
صورة لجاكوب أوينزألقِ نظرة حولك ، واستكشف البدائل ، ويرجى التوقف عن استخدام System.Drawing في ASP.NET ، باسم حب القطط.