تحويل Stream إلى Byte Array: دليل لمطور C# (إصدار 2025)
\n\nهل سبق لك أن أمسكت بنفسك تفكر: "لماذا، يا C#، لا تسمح لي ببساطة بأخذ هذا Stream وتحويله إلى مصفوفة بايت بسطر واحد؟". إذا كان الأمر كذلك، فأنت لست وحدك. سؤال "إنشاء مصفوفة بايت من stream" على Stack Overflow يجمع آلاف المشاهدات، وهذا ليس من فراغ. تبدو المهمة بسيطة، لكن الشيطان، كما هو الحال دائمًا، يكمن في التفاصيل: الأداء، الذاكرة، عدم التزامن، وإصدارات .NET.
في عام 2025، تقدم لنا بيئة .NET العديد من الحلول الأنيقة. في هذه المقالة، سنستعرض 5 أفضل الممارسات - من الكلاسيكيات إلى الجديد. ستتعرف على متى تستخدم MemoryStream، ولماذا لا يزال BinaryReader مناسبًا، وكيف لا تتسبب في تعطل التطبيق عند العمل مع تدفقات ضخمة. هيا بنا!
1. كلاسيكي من نوعه: MemoryStream وCopyTo
\n\nالطريقة الأكثر شيوعًا وبديهية. إذا كان لديك Stream (مثلاً من ملف أو شبكة أو ذاكرة)، فما عليك سوى "تفريغه" في MemoryStream، ثم استدعاء ToArray().
public static byte[] StreamToBytesUsingMemoryStream(Stream inputStream)\n{\n using (var memoryStream = new MemoryStream())\n {\n inputStream.CopyTo(memoryStream);\n return memoryStream.ToArray();\n }\n}\n\nالمزايا:
\n- \n
- البساطة وسهولة القراءة. \n
- يعمل في جميع إصدارات .NET (Framework, Core, 5+). \n
- يدعم
CopyToAsyncللعمل غير المتزامن. \n
العيوب:
\n- \n
- إذا كان التدفق ضخمًا (مثلاً 1 جيجابايت)، فستحتفظ بالبيانات في الذاكرة مرتين: أولاً في
MemoryStream، ثم في المصفوفة. \n - ليس الخيار الأسرع للتدفقات الصغيرة جدًا (عبء إضافي لإنشاء الكائن). \n
متى تستخدم: للتدفقات متوسطة الحجم (حتى 100 ميجابايت) وعندما تكون سهولة قراءة الكود أهم من التحسينات الدقيقة.
\n\n2. البطل غير المتزامن: ReadAsync وBuffer
\n\nفي التطوير الحديث، عدم التزامن هو المعيار. تسمح طريقة ReadAsync بقراءة البيانات على دفعات دون حظر التدفق. هذا مفيد بشكل خاص في ASP.NET Core أو تطبيقات الواجهة الرسومية.
public static async Task<byte[]> StreamToBytesAsync(Stream inputStream)\n{\n using (var memoryStream = new MemoryStream())\n {\n byte[] buffer = new byte[8192]; // 8 كيلوبايت - الحجم القياسي\n int bytesRead;\n while ((bytesRead = await inputStream.ReadAsync(buffer, 0, buffer.Length)) > 0)\n {\n await memoryStream.WriteAsync(buffer, 0, bytesRead);\n }\n return memoryStream.ToArray();\n }\n}\n\nنصيحة احترافية: يمكن تغيير حجم المخزن المؤقت. للتدفقات الشبكية، 8 كيلوبايت هي النقطة المثالية. لأقراص SSD، يمكن زيادته إلى 64 كيلوبايت.
\n\nالمزايا:
\n- \n
- لا يحجب التدفق المستدعي. \n
- يسمح بمعالجة البيانات "أثناء التنقل" (مثلاً حساب التجزئة). \n
- التحكم في استهلاك الذاكرة. \n
العيوب:
\n- \n
- كود أكثر قليلاً. \n
- يتطلب فهم نمط async/await. \n
3. BinaryReader: صديق قديم أفضل من صديقين جديدين
\n\nكثيرون ينسون BinaryReader، وهذا خطأ. إنه قادر على قراءة التدفق بأكمله بأمر واحد - ReadBytes. لكن هناك فارق بسيط: يحتاج إلى معرفة طول التدفق مسبقًا.
public static byte[] StreamToBytesUsingBinaryReader(Stream inputStream)\n{\n if (inputStream.CanSeek)\n {\n inputStream.Position = 0; // إعادة تعيين الموضع إذا لزم الأمر\n using (var reader = new BinaryReader(inputStream, Encoding.UTF8, leaveOpen: true))\n {\n return reader.ReadBytes((int)inputStream.Length);\n }\n }\n else\n {\n throw new InvalidOperationException("التدفق لا يدعم البحث");