مع
تنزيلات الملفات الكبيرة في تطبيقات الويب أمرًا صعبًا دائمًا، لذلك بالنسبة لمعظم المواقع، يقع الويل على المستخدم إذا تمت مقاطعة التنزيل. ولكن ليس علينا القيام بذلك الآن، لأنه يمكنك جعل تطبيق ASP.NET الخاص بك قادرًا على دعم التنزيلات القابلة للاستئناف (المستمرة) للملفات الكبيرة. باستخدام الطريقة المتوفرة في هذه المقالة، يمكنك تتبع عملية التنزيل، حتى تتمكن من التعامل مع الملفات التي تم إنشاؤها ديناميكيًا - والقيام بذلك دون الحاجة إلى مكتبات الارتباط الديناميكي ISAPI القديمة ورمز C++ غير المُدار.
من الأسهل تقديم خدمة للعملاء لتنزيل الملفات من الإنترنت، أليس كذلك؟ ما عليك سوى نسخ الملف القابل للتنزيل إلى دليل تطبيق الويب الخاص بك، ونشر الرابط والسماح لـ IIS بالقيام بجميع الأعمال ذات الصلة. ومع ذلك، لا ينبغي أن يكون تقديم الملفات أكثر من مجرد ألم في الرقبة، فأنت لا تريد أن يتمكن العالم كله من الوصول إلى بياناتك، ولا تريد أن يكون خادمك مسدودًا بمئات الملفات الثابتة، فأنت لا تريد ذلك. لا تريد حتى تنزيل الملفات المؤقتة - يتم إنشاء هذه الملفات فقط أثناء وقت الخمول بعد أن يبدأ العميل في التنزيل.
لسوء الحظ، ليس من الممكن تحقيق هذه التأثيرات باستخدام استجابة IIS الافتراضية لطلبات التنزيل. لذلك بشكل عام، للتحكم في عملية التنزيل، يحتاج المطورون إلى الارتباط بصفحة aspx. مخصصة حيث يتحققون من بيانات اعتماد المستخدم، وينشئون ملفًا قابلاً للتنزيل ويستخدمون التعليمات البرمجية التالية لدفع الملف إلى العميل:
Response.WriteFile
Response.End()
وهنا تظهر المشكلة الحقيقية.
ما هي المشكلة؟
تبدو طريقة WriteFile مثالية، فهي تجعل البيانات الثنائية للملف تتدفق إلى العميل. لكن ما لم نكن نعرفه حتى وقت قريب هو أن طريقة WriteFile هي طريقة سيئة السمعة لاستهلاك الذاكرة، حيث تقوم بتحميل الملف بأكمله في ذاكرة الوصول العشوائي الخاصة بالخادم لخدمته (في الواقع يستغرق ضعف حجم الملف). بالنسبة للملفات الكبيرة، قد يتسبب ذلك في حدوث مشكلات في ذاكرة الخدمة وربما تكرار عمليات ASP.NET. ولكن في يونيو 2004، أصدرت Microsoft تصحيحًا أدى إلى حل المشكلة. أصبح هذا التصحيح الآن جزءًا من حزمة الخدمة .NET Framework 1.1 (SP1).
يقدم هذا التصحيح طريقة TransmitFile، التي تقرأ ملف القرص في مخزن مؤقت أصغر للذاكرة ثم تبدأ في نقل الملف. على الرغم من أن هذا الحل يحل مشاكل الذاكرة والحلقة، إلا أنه لا يزال غير مرضٍ. ليس لديك أي سيطرة على دورة حياة الاستجابة. ليس لديك طريقة لمعرفة ما إذا كان التنزيل قد اكتمل بشكل صحيح، وليس لديك طريقة لمعرفة ما إذا كان التنزيل قد تمت مقاطعته، و(إذا قمت بإنشاء ملفات مؤقتة) ليس لديك طريقة لمعرفة ما إذا كان يجب عليك حذف الملفات ومتى. ومما يزيد الطين بلة، أنه في حالة فشل التنزيل، تبدأ طريقة TransmitFile في التنزيل من بداية الملف الذي يحاول العميل بعد ذلك القيام به.
أحد الحلول الممكنة، وهو تنفيذ خدمة النقل الذكي في الخلفية (BITS)، ليس ممكنًا بالنسبة لمعظم المواقع لأنه قد يتعارض مع غرض الحفاظ على استقلالية متصفح العميل ونظام التشغيل.
يأتي أساس الحل المرضي من المحاولة الأولى لشركة Microsoft لحل مشكلة ارتباك الذاكرة الناتجة عن WriteFile (راجع مقالة قاعدة المعارف رقم 812406). أظهرت هذه المقالة عملية تنزيل بيانات ذكية تقرأ البيانات من دفق الملفات. قبل أن يرسل الخادم مجموعة البايت إلى العميل، فإنه يستخدم خاصية Response.IsClientConnected للتحقق مما إذا كان العميل لا يزال متصلاً. إذا كان الاتصال لا يزال مفتوحًا، فإنه يستمر في إرسال بايتات الدفق، وإلا فإنه يتوقف لمنع الخادم من إرسال البيانات غير الضرورية.
هذا هو النهج الذي نتبعه، خاصة عند تنزيل الملفات المؤقتة. في حالة قيام IsClientConnected بإرجاع False، فأنت تعلم أن عملية التنزيل تمت مقاطعتها ويجب عليك حفظ الملف؛ وإلا، عند اكتمال العملية بنجاح، تقوم بحذف الملف المؤقت. بالإضافة إلى ذلك، من أجل استئناف التنزيل المتقطع، كل ما عليك فعله هو بدء التنزيل من النقطة التي فشل فيها اتصال العميل أثناء آخر محاولة تنزيل.
دعم بروتوكول HTTP ومعلومات الرأس (الرأس)
يمكن استخدام دعم بروتوكول HTTP للتعامل مع معلومات الرأس للتنزيلات المتقطعة. باستخدام عدد صغير من رؤوس HTTP، يمكنك تحسين عملية التنزيل الخاصة بك لتتوافق تمامًا مع مواصفات بروتوكول HTTP. توفر هذه المواصفات، بالإضافة إلى النطاقات، كافة المعلومات اللازمة لاستئناف التنزيل الذي تمت مقاطعته.
وإليك كيف يعمل. أولاً، إذا كان الخادم يدعم التنزيلات القابلة للاستئناف من جانب العميل، فإنه يرسل رأس قبول النطاقات في الاستجابة الأولية. يرسل الخادم أيضًا رأس علامة كيان (ETag)، والذي يحتوي على سلسلة تعريف فريدة.
يعرض الكود الموجود أدناه بعض الرؤوس التي يرسلها IIS إلى العميل استجابة لطلب تنزيل أولي، والذي يقوم بتمرير تفاصيل الملف المطلوب إلى العميل.
HTTP/1.1200 موافق
الاتصال: قريب
التاريخ: الثلاثاء 19 أكتوبر 2004 الساعة 15:11:23 بتوقيت جرينتش
قبول النطاقات: بايت
آخر تعديل: الأحد، 26 سبتمبر 2004، الساعة 15:52:45 بتوقيت جرينتش
ETag: "47febb2cfd76c41:2062"
التحكم في ذاكرة التخزين المؤقت: خاص
نوع المحتوى: application/x-zip-compressed
طول المحتوى: 2844011
بعد تلقي معلومات الرأس هذه، إذا تمت مقاطعة التنزيل، فسيقوم متصفح IE بإرسال قيمة Etag ومعلومات رأس النطاق مرة أخرى إلى الخادم في طلبات التنزيل اللاحقة. يعرض الكود أدناه بعض الرؤوس التي يرسلها IE إلى الخادم عند محاولة استئناف التنزيل الذي تمت مقاطعته.
GET
تشير هذه الرؤوس إلى أن IE قام بتخزين علامة الكيان التي يوفرها IIS مؤقتًا وأرسلها مرة أخرى إلى الخادم في رأس If-Range، وهذه طريقة لضمان استئناف التنزيل من نفس الملف بالضبط. لسوء الحظ، لا تعمل جميع المتصفحات بنفس الطريقة. قد تكون رؤوس HTTP الأخرى التي يرسلها العميل للتحقق من الملف هي If-Match، أو If-Unmodified-Since، أو Unless-Modified-Since. من الواضح أن المواصفات ليست واضحة بشأن الرؤوس التي يجب أن يدعمها برنامج العميل، أو الرؤوس التي يجب استخدامها. لذلك، لا يستخدم بعض العملاء معلومات الرأس على الإطلاق، بينما يستخدم IE فقط If-Range وUnless-Modified-Since. من الأفضل التحقق من هذه المعلومات باستخدام الكود. عند اتباع هذا النهج، يمكن أن يتوافق تطبيقك مع مواصفات HTTP على مستوى عالٍ جدًا ويعمل مع مجموعة متنوعة من المتصفحات. يحدد رأس النطاق نطاق البايت المطلوب - في هذه الحالة هو نقطة البداية التي يجب على الخادم استئناف تدفق الملفات منها.
عندما يتلقى IIS نوع طلب تنزيل السيرة الذاتية، فإنه يرسل استجابة تحتوي على معلومات الرأس التالية:
HTTP/1.1 206 Partial Content
نطاق المحتوى: البايتات 822603-2844010/2844011
قبول النطاقات: بايت
آخر تعديل: الأحد، 26 سبتمبر 2004، الساعة 15:52:45 بتوقيت جرينتش
ETag: "47febb2cfd76c41:2062"
التحكم في ذاكرة التخزين المؤقت: خاص
نوع المحتوى: application/x-zip-compressed
طول المحتوى: 2021408
يرجى ملاحظة أن الكود أعلاه له استجابة HTTP مختلفة قليلاً عن طلب التنزيل الأصلي - طلب استئناف التنزيل هو 206 بينما كان طلب التنزيل الأصلي 200. وهذا يدل على أن ما يتم تمريره عبر السلك هو ملف جزئي. هذه المرة يشير رأس Content-Range إلى العدد الدقيق للبايتات التي يتم تمريرها وموقعها.
IE انتقائي جدًا بشأن معلومات الرأس هذه. إذا كانت الاستجابة الأولية لا تحتوي على معلومات رأس Etag، فلن يحاول IE مطلقًا استئناف التنزيل. العملاء الآخرون الذين اختبرتهم لا يستخدمون رأس ETag، بل يعتمدون ببساطة على اسم الملف ونطاق الطلب ويستخدمون رأس Last-Modified إذا كانوا يحاولون التحقق من صحة الملف.
فهم متعمق لبروتوكول HTTP
تعتبر معلومات الرأس الموضحة في القسم السابق كافية لجعل الحل الخاص باستئناف التنزيلات يعمل، ولكنها لا تغطي مواصفات HTTP بالكامل.
يمكن لرأس النطاق أن يطلب نطاقات متعددة في طلب واحد، وهي ميزة تسمى "النطاقات متعددة الأجزاء". يجب عدم الخلط بينه وبين التنزيل المجزأ، حيث تستخدم جميع أدوات التنزيل تقريبًا التنزيل المجزأ لزيادة سرعات التنزيل. تدعي هذه الأدوات أنها تزيد من سرعات التنزيل عن طريق فتح اتصالين متزامنين أو أكثر، يطلب كل منهما نطاقًا مختلفًا من الملفات.
لا تفتح فكرة النطاقات متعددة الأجزاء اتصالات متعددة، ولكنها تسمح لبرنامج العميل بطلب أول عشرة بايتات وآخر عشر بايتات من الملف في دورة طلب/استجابة واحدة.
بصراحة، لم أجد أي برنامج يستخدم هذه الميزة. لكنني أرفض كتابة "إنه غير متوافق تمامًا مع HTTP" في إعلان الكود. سيؤدي حذف هذه الميزة إلى انتهاك قانون مورفي بالتأكيد. وبغض النظر عن ذلك، تُستخدم النطاقات متعددة الأجزاء في عمليات إرسال البريد الإلكتروني لفصل معلومات الرأس والنص العادي والمرفقات.
نموذج التعليمات البرمجية
نحن نعرف كيف يتبادل العميل والخادم معلومات الرأس لضمان التنزيلات القابلة للاستئناف، ومن خلال الجمع بين هذه المعرفة وفكرة تدفق كتلة الملفات، يمكنك إضافة إمكانات موثوقة لإدارة التنزيلات إلى تطبيقات ASP.NET الخاصة بك.
تتمثل طريقة التحكم في عملية التنزيل في اعتراض طلب التنزيل من العميل وقراءة معلومات الرأس والاستجابة بشكل مناسب. قبل .NET، كان عليك كتابة تطبيق ISAPI (Internet Server API) لتنفيذ هذه الوظيفة، ولكن مكون .NET Framework يوفر واجهة IHttpHandler التي، عند تنفيذها في فصل دراسي، تسمح لك بالقيام بذلك باستخدام اعتراض كود .NET فقط ومعالجة الطلبات. وهذا يعني أن تطبيقك يتمتع بالتحكم الكامل والاستجابة لعملية التنزيل ولا يتضمن أو يستخدم أبدًا وظائف IIS الآلية.
يتضمن نموذج التعليمات البرمجية فئة HttpHandler مخصصة (ZIPHandler) في ملف HttpHandler.vb. يقوم ZipHandler بتنفيذ واجهة IhttpHandler ويتعامل مع الطلبات الخاصة بجميع ملفات .zip.
لاختبار نموذج التعليمات البرمجية، تحتاج إلى إنشاء دليل ظاهري جديد في IIS ونسخ الملفات المصدر هناك. قم بإنشاء ملف يسمى download.zip في هذا الدليل (يرجى ملاحظة أن IIS وASP.NET لا يمكنهم التعامل مع التنزيلات الأكبر من 2 جيجابايت، لذا تأكد من أن ملفك لا يتجاوز هذا الحد). قم بتكوين دليل IIS الظاهري لتعيين ملحق .zip من خلال aspnet_isapi.dll.
فئة HttpHandler: بعد قيام ZIPHandler
بتعيين ملحق .zip في ASP.NET، في كل مرة يطلب فيها العميل ملف .zip من الخادم، يستدعي IIS أسلوب ProcessRequest لفئة ZipHandler (راجع رمز التنزيل).
تقوم طريقة ProcessRequest أولاً بإنشاء مثيل لفئة FileInformation المخصصة (راجع رمز التنزيل)، والتي تتضمن حالة التنزيل (مثل قيد التقدم، أو متقطع، وما إلى ذلك). يقوم المثال بترميز المسار إلى ملف نموذج download.zip في التعليمات البرمجية. إذا قمت بتطبيق هذا الرمز على التطبيق الخاص بك، فسوف تحتاج إلى تعديله لفتح الملف المطلوب.
استخدم objRequest لاكتشاف الملف المطلوب، واستخدم الملف لفتح objFile.
' على سبيل المثال objFile = New Download.FileInformation(<اسم الملف الكامل>)
objFile = تنزيل جديد. معلومات الملف ( _
objContext.Server.MapPath("~/download.zip"))
بعد ذلك، يقوم البرنامج بتنفيذ الطلب باستخدام رؤوس HTTP الموضحة (إذا تم توفير الرؤوس في الطلب، فقد صليت لأول مرة تحت الشمس). إذا فشل التحقق من الصحة، فسيتم إنهاء الاستجابة على الفور ويتم إرسال قيمة رمز الحالة المناسبة.
إذا لم يكن objRequest.HttpMethod.Equals(HTTP_METHOD_GET) أم لا
objRequest.HttpMethod.Equals(HTTP_METHOD_HEAD) ثم
' حاليًا يتم دعم أساليب GET و HEAD فقط objResponse.StatusCode = 501 ' لم يتم التنفيذ
ElseIf Not objFile.Exists إذن
' تعذر العثور على الملف المطلوب objResponse.StatusCode = 404 ' غير موجود
ElseIf objFile.Length > Int32.MaxValue ثم
'الملف كبير جدًا objResponse.StatusCode = 413' كيان الطلب كبير جدًا
ElseIf Not ParseRequestHeaderRange(objRequest, alRequestedRangesBegin, alRequestedRangesend, _
objFile.Length، bIsRangeRequest) ثم
' يحتوي طلب النطاق على كيانات عديمة الفائدة objResponse.StatusCode = 400 ' طلب عديم الفائدة
ElseIf Not CheckIfModifiedSince(objRequest,objFile) ثم
'لم يتم تعديل الكيان objResponse.StatusCode = 304 'لم يتم تعديل الكيان
ElseIf Not CheckIfUnmodifiedSince(objRequest,objFile) ثم
' تم تعديل الكيان منذ آخر تاريخ طلب objResponse.StatusCode = 412 ' فشلت المعالجة المسبقة
ElseIf Not CheckIfMatch(objRequest, objFile) ثم
' الكيان لا يطابق الطلب objResponse.StatusCode = 412 ' فشلت المعالجة المسبقة
ElseIf Not CheckIfNoneMatch(objRequest, objResponse,objFile) ثم
"الكيان لا يطابق طلب عدم التطابق.
'رمز الاستجابة موجود في وظيفة CheckIfNoneMatch
آخر
'"تم الفحص الأولي بنجاح"'
تتحقق وظيفة ParseRequestHeaderRange في هذه الاختبارات الأولية (راجع رمز التنزيل) مما إذا كان العميل قد طلب نطاق ملف (مما يعني تنزيلًا جزئيًا). إذا كان النطاق المطلوب غير صالح (النطاق غير الصالح هو قيمة نطاق تتجاوز حجم الملف أو تحتوي على رقم غير معقول)، تقوم هذه الطريقة بتعيين bIsRangeRequest إلى True. إذا تم طلب نطاق، فإن أسلوب CheckIfRange يتحقق من معلومات رأس IfRange.
إذا كان النطاق المطلوب صالحًا، يقوم الكود بحساب حجم رسالة الاستجابة. إذا طلب العميل نطاقات متعددة، فستتضمن قيمة حجم الاستجابة قيمة طول الرأس متعدد الأجزاء.
إذا تعذر تحديد قيمة الرأس المرسلة، فسيتعامل البرنامج مع طلب التنزيل كطلب أولي بدلاً من التنزيل الجزئي، وإرسال دفق تنزيل جديد يبدأ من أعلى الملف.
إذا كان bIsRangeRequest AndAlso CheckIfRange(objRequest, objFile) إذن
'هذا طلب نطاق' إذا كان صفيف النطاق يحتوي على كيانات متعددة، فهو أيضًا طلب نطاق متعدد الأجزاء bMultipart = CBool(alRequestedRangesBegin.GetUpperBound(0)>0)
'انتقل إلى كل نطاق للحصول على طول الاستجابة بالكامل لـ iLoop = alRequestedRangesBegin.GetLowerBound(0) إلى alRequestedRangesBegin.GetUpperBound(0)
'طول المحتوى (في هذا النطاق)
iResponseContentLength += Convert.ToInt32(alRequestedRangesend( _
iLoop) - alRequestedRangesBegin(iLoop)) + 1
إذا بمتعدد الأجزاء ثم
' إذا كان طلب نطاق متعدد الأجزاء، فاحسب طول معلومات الرأس المتوسطة التي سيتم إرسالها iResponseContentLength += MULTIPART_BOUNDARY.Length
iResponseContentLength += objFile.ContentType.Length
iResponseContentLength += alRequestedRangesBegin(iLoop).ToString.Length
iResponseContentLength += alRequestedRangesend(iLoop).ToString.Length
iResponseContentLength += objFile.Length.ToString.Length
' 49 هو طول فواصل الأسطر والأحرف الضرورية الأخرى في التنزيلات متعددة الأجزاء iResponseContentLength += 49
نهاية إذا
التالي iLoop
إذا bMultipart ثم
'إذا كان طلب نطاق متعدد الأجزاء،
' يجب علينا أيضًا حساب طول الرأس الوسيط الأخير الذي سيتم إرساله iResponseContentLength +=MULTIPART_BOUNDARY.Length
'8 هو طول الشرطة والسطر الجديد iResponseContentLength += 8
آخر
' ليس تنزيلًا متعدد الأجزاء، لذا يجب علينا تحديد نطاق الاستجابة لرأس HTTP الأولي objResponse.AppendHeader( HTTP_HEADER_CONTENT_RANGE, "bytes " & _
alRequestedRangesBegin(0).ToString & "-" & _
alRequestedRangesend(0).ToString & "/" & _
objFile.Length.ToString)
'انتهى إذا
'استجابة النطاق objResponse.StatusCode = 206' استجابة جزئية أخرى
' هذا ليس طلب نطاق، أو أن معرف كيان النطاق المطلوب لا يتطابق مع معرف الكيان الحالي،
تشير عبارة "لذا ابدأ تنزيلًا جديدًا" إلى أن حجم الجزء المكتمل من الملف يساوي طول المحتوى iResponseContentLength =Convert.ToInt32(objFile.Length)
'العودة إلى حالة موافق العادية objResponse.StatusCode = 200
نهاية إذا
بعد ذلك، يجب على الخادم إرسال عدة رؤوس استجابة مهمة، مثل طول المحتوى وEtag ونوع محتوى الملف:
'اكتب طول المحتوى في الاستجابة objResponse.AppendHeader( HTTP_HEADER_CONTENT_LENGTH,iResponseContentLength.ToString)
' اكتب تاريخ التعديل الأخير في الاستجابة objResponse.AppendHeader( HTTP_HEADER_LAST_MODIFIED,objFile.LastWriteTimeUTC.ToString("r"))
' أخبر برنامج العميل بأننا قبلنا طلب النطاق objResponse.AppendHeader( HTTP_HEADER_ACCEPT_RANGES,HTTP_HEADER_ACCEPT_RANGES_BYTES)
'اكتب علامة كيان الملف في الاستجابة (محاطة بعلامتي اقتباس)
objResponse.AppendHeader(HTTP_HEADER_ENTITY_TAG, """" & objFile.EntityTag & """")
'اكتب نوع المحتوى إلى ResponseIf bMultipart ثم
'الرسائل متعددة الأجزاء لها هذا النوع الخاص' في المثال، تتم كتابة نوع mime الفعلي للملف إلى الاستجابة لاحقًا objResponse.ContentType = MULTIPART_CONTENTTYPE
آخر
'نوع محتوى الملف المملوك لرسالة جزئية واحدة objResponse.ContentType = objFile.ContentType
نهاية إذا
كل ما تحتاجه للتنزيل جاهز ويمكنك البدء في تنزيل الملفات. ستستخدم كائن FileStream لقراءة أجزاء من البايت من ملف. قم بتعيين خاصية الحالة لمثيل FileInformation objFile على fsDownloadInProgress. وطالما ظل العميل متصلاً، يقرأ الخادم أجزاء من البايتات من الملف ويرسلها إلى العميل. بالنسبة للتنزيلات متعددة الأجزاء، يرسل هذا الرمز معلومات رأس محددة. إذا تم قطع اتصال العميل، يقوم الخادم بتعيين حالة الملف إلى fsDownloadBroken. إذا أكمل الخادم إرسال النطاق المطلوب، فإنه يضبط الحالة على fsDownloadFinished (راجع رمز التنزيل).
فئة مساعدة FileInformation
في قسم ZIPHandler ستجد أن FileInformation هي فئة مساعدة تتضمن معلومات حالة التنزيل (مثل التنزيل والمقاطعة وما إلى ذلك).
لإنشاء مثيل FileInformation، تحتاج إلى تمرير المسار إلى الملف المطلوب إلى مُنشئ الفئة:
Public Sub New(ByVal sPath As String)
m_objFile = System.IO.FileInfo(sPath) الجديد
End Sub
FileInformation كائن System.IO.FileInfo للحصول على معلومات الملف، والتي يتم عرضها كخصائص للكائن (مثل ما إذا كان الملف موجودًا، أو الاسم الكامل للملف، أو الحجم، وما إلى ذلك). تعرض هذه الفئة أيضًا تعداد DownloadState، الذي يصف الحالات المختلفة لطلب التنزيل:
Enum DownloadState
'مسح: لا توجد عملية تنزيل، قد يكون الملف يحتفظ بـ fsClear = 1
'مقفل: لا يمكن تغيير الملفات التي تم إنشاؤها ديناميكيًا fsLocked = 2
'قيد التقدم: الملف مقفل وعملية التنزيل جارية fsDownloadInProgress = 6
'معطل: الملف مقفل، وكانت عملية التنزيل جارية، ولكن تم إلغاؤها fsDownloadBroken = 10
' انتهى: تم قفل الملف واكتملت عملية التنزيل fsDownloadFinished = 18
End Enum
FileInformation أيضًا قيمة السمة EntityTag. تم ترميز هذه القيمة بشكل ثابت في كود المثال لأن كود المثال يستخدم ملف تنزيل واحد فقط ولن يتم تغيير هذا الملف، ولكن بالنسبة للتطبيق الحقيقي، ستوفر ملفات متعددة، حتى ديناميكيًا لإنشاء الملفات، يجب أن يوفر الكود الخاص بك قيمة EntityTag الفريدة لكل ملف. بالإضافة إلى ذلك، يجب أن تتغير هذه القيمة في كل مرة يتم فيها تغيير الملف أو تعديله. يتيح ذلك لبرنامج العميل التحقق من أن أجزاء البايت التي تم تنزيلها لا تزال محدثة. فيما يلي جزء من نموذج التعليمات البرمجية الذي يُرجع قيمة EntityTag المضمنة:
Public ReadOnly Property EntityTag() As String
يتم استخدام EntityTag للاستجابة الأولية (200) للعميل ولطلبات الاسترداد من العميل.
'قم بإنشاء سلسلة فريدة للملف.
لاحظ أنه طالما لم يتغير الملف، يجب الاحتفاظ بالرمز الفريد.
ومع ذلك، إذا تغير الملف أو تم تعديله، فيجب تغيير هذا الرمز.
إرجاع "MyExampleFileID"
نهاية الحصول على
خاصية النهاية
قد تتكون علامة EntityTag البسيطة والآمنة بشكل عام من اسم الملف وتاريخ آخر تعديل للملف. بغض النظر عن الطريقة التي تستخدمها، يجب عليك التأكد من أن هذه القيمة فريدة حقًا ولا يمكن الخلط بينها وبين EntityTag للملفات الأخرى. أرغب في تسمية الملفات التي تم إنشاؤها في تطبيقي ديناميكيًا بواسطة العميل والعميل وفهرس الرمز البريدي، وتخزين المعرف الفريد العمومي (GUID) المستخدم باعتباره EntityTag في قاعدة البيانات.
تقوم فئة ZipFileHandler بقراءة وتعيين خاصية الدولة العامة. بعد الانتهاء من التنزيل، يقوم بتعيين الحالة على fsDownloadFinished. في هذا الوقت يمكنك حذف الملفات المؤقتة. هنا تحتاج بشكل عام إلى استدعاء طريقة الحفظ للحفاظ على الحالة.
حالة الملكية العامة () كحالة التنزيل
يحصل
العودة m_nState
نهاية الحصول على
تعيين (ByVal nState كـ DownloadState)
m_nState = nState
' إجراء اختياري: يمكنك حذف الملف تلقائيًا في هذا الوقت.
إذا تم تعيين الحالة على "انتهى"، فلن تحتاج بعد الآن إلى هذا الملف.
'إذا كان nState =DownloadState.fsDownloadFinished إذن
'واضح()
'آخر
'يحفظ()
'انتهى إذا
يحفظ()
نهاية المجموعة
خاصية النهاية
ZipFileHandler استدعاء الأسلوب Save في أي وقت تتغير فيه حالة الملف لحفظ حالة الملف بحيث يمكن عرضه للمستخدم لاحقًا. يمكنك أيضًا استخدامه لحفظ EntityTag الذي أنشأته بنفسك. يرجى عدم حفظ حالة الملف وقيمة EntityTag في التطبيق أو الجلسة أو ذاكرة التخزين المؤقت - يجب عليك حفظ المعلومات عبر دورة حياة كل هذه الكائنات.
برايفتسوبسيف ()
'احفظ حالة تنزيل الملف في قاعدة البيانات أو ملف XML.
بالطبع، إذا لم تقم بإنشاء الملف ديناميكيًا، فلن تحتاج إلى حفظ هذه الحالة.
End Sub
كما ذكرنا سابقًا، فإن رمز المثال يتعامل فقط مع ملف موجود (download.zip)، ولكن يمكنك تحسين هذا البرنامج بشكل أكبر لإنشاء الملف المطلوب حسب الحاجة.
عند اختبار نموذج التعليمات البرمجية، قد يكون نظامك المحلي أو شبكة LAN الخاصة بك سريعة جدًا بحيث لا يمكنها مقاطعة عملية التنزيل، لذلك أوصي باستخدام اتصال LAN بطيء (تقليل عرض النطاق الترددي للموقع في IIS هو أسلوب محاكاة) أو وضع الخادم على وضع التشغيل الإنترنت.
لا يزال تنزيل الملفات على العميل يمثل تحديًا. يمكن أن يؤدي خادم ذاكرة التخزين المؤقت للويب غير الصحيح أو الذي تم تكوينه بشكل خاطئ والذي يتم تشغيله بواسطة مزود خدمة الإنترنت إلى فشل تنزيلات الملفات الكبيرة، بما في ذلك ضعف أداء التنزيل أو الإنهاء المبكر للجلسة. إذا تجاوز حجم الملف 255 ميجابايت، فيجب عليك تشجيع العملاء على استخدام برامج إدارة التنزيل التابعة لجهات خارجية، على الرغم من أن بعض المتصفحات الحديثة تحتوي على مدير تنزيل أساسي مدمج.
إذا كنت ترغب في توسيع رمز المثال بشكل أكبر، فقد يكون من المفيد استشارة مواصفات HTTP. يمكنك إنشاء مجاميع اختبارية MD5 للتنزيلات، وإضافتها باستخدام رأس Content-MD5 لتوفير طريقة للتحقق من سلامة الملفات التي تم تنزيلها. لا يتضمن نموذج التعليمات البرمجية أساليب HTTP أخرى باستثناء GET وHEAD.