ماژول دانلودر برای ASP.NET

۱۳۹۰/۱۲/۰۶ ۱۳:۰۴ Salar Khalilzadeh https://plus.google.com/105397214522932500988 منتشر شده در تاریخ : ۱۳۹۰/۱۲/۰۶ دسته بندی : ، ، ، 3

  • مقدمه
بیشتر مواقع لازم است تا بر روی دانلود های وبسایت نظارت انجام شود, برای مثال فقط اعضا اجازه دانلود فایلی را داشته و یا دانلود یک شمارنده داشته باشد و غیره. در حالت عادی برنامه نویسان فایل را مستقیم به خروجی می نویسند. این روش کار را انجام داده و به نتیجه لازم نیز می رسد اما یک مزیت بزرگ را از دست می رود. آن مزیت قابلیت Resumable Downloading است که به طور خلاصه به کاربر این اجازه را می دهد که در هر زمان دانلود را متوقف کرده و پس از شروع مجدد از همان محل قبلی ادامه دانلود را انجام دهد. این موارد بیشتر در فایلهای حجیم صادق است و مورد نیاز می شود.
همچنین یک نیاز دیگر هم پیش می آید و آن محدود کردن پهنای باند مصرفی برای دانلود است. که در حالت عادی و ارسال فایل از طریق دستورات asp.net این کار امکان پذیر نیست.

  • معرفی
برای رفع مشکلات و محدودیت های مطرح شده ماژولی را آماده کرده ام که با استفاده از آنها میتوانید کنترل نسبتا کاملی بر دانلود کاربر داشته باشید.
مجموعه کلاسهای ResumableDownload چندین کار مخلتف را انجام می دهد که با کنار هم قرار دادن آنها به مقصودمان می رسیم.

  • طریقه استفاده
قبل از هرکاری باید درخواست ورودی کاربر بررسی شده و دقیقا بدانیم که کدام فایل مد نظر است. سپس حجم آن را بدست بیاوریم.
اولین قدیم شناسایی درخواست کاربر است. این درخواست معمولا توسط یک نرم افزار دانلودر مانند IDM انجام شده است. هدف در اینجا شناسایی هدر های ارسالی است که توسط یکی از متدهای کلاس HeadersParser انجام می شود.
قدم دوم باید اطلاعات فایلی که قرار است دانلود بشود توسط کلاس DownloadDataInfo جمع آوری شود. یکی از سازنده های کلاس اطلاعات فایل را دریافت می کند باید مورد استفاده قرار گیرد. سپس خروجی متد مورد استفاده از HeadersParser باید به کلاس DownloadDataInfo اطلاع داده شود و این کار با استفاده از متد InitializeRanges آن انجام می شود.
قدم سوم بررسی صحت درخواست دانلود است. این کار توسط متد های Validate کلاس HeadersParser انجام می شود. این کار از این جهت مهم است که ممکن است فایل در سرور تغییر داده شده باشد ولی کاربر سعی دارد تا همچنان به دانلود نسخه قدیمی که ناقص انجام شده بود ادامه دهد.
قدم اختیاری در این بین اعمال محدودیت در پهنای باند دانلود فایل برای کاربر است. این محدودیت توسط کلاس UserSpeedLimitManager قابل اعمال است. در آزمایشاتی که من انجام دادم مشخص شد که در هر صورت استفاده از این کلاس بار زیادی را از دوش سرور بر می دارد. این بدان علت است که در حالت بدون محدودیت سرور تمام فایل را از دیسک خوانده و به یکباره به بافر انتظار می فرستد, این عملیات یکباره بار زیادی بر سرور وارد می کند. همچنین مشخص شد که گذاشتن محدودیت حتی در حد 2MB/s می تواند تاثیر زیادی در افزایش بازدهی سرور داشته باشد.
قدم نهایی ارسال فایل به کاربر طبق درخواست وی که از طرق متد ProcessDownload کلاس DownloadProcess انجام می شود.

در ادامه یک مثال کامل را مشاهده می کنید. در این مثال نام فایل به عنوان ورودی ارسال می شود, سیستم این فایل را از مسیر برنامه یافته و برای کاربر ارسال می کند.
برای مثال آدرس به این صورت خواهد بود: http://localhost:5200/ResumableDownload.ashx?file=sample.zip

// 50 KB limit
const int DownloadLimit = 50 * 1024;

public void ProcessRequest(HttpContext context)
{
 // Accepting user request

 // reading the query
 var fileNameQuery = context.Request.QueryString["file"];

 // validating the request
 if (string.IsNullOrEmpty(fileNameQuery))
 {
  InvalidRequest(context, "Invalid request! Specify file name in url e.g.: ResumableDownload.ashx?file=sample.zip");
  return;
 }

 // the physical file address path
 var fileName = context.Server.MapPath(fileNameQuery);
 if (!File.Exists(fileName))
 {
  InvalidRequest(context, "File does not exists!");
  return;
 }

 // reading file info
 var fileInfo = new FileInfo(fileName);
 var fileLength = fileInfo.Length;

 // Download information class
 var downloadInfo = new DownloadDataInfo(fileName);

 // Reading request download range
 var requestedRanges = HeadersParser.ParseHttpRequestHeaderMultipleRange(context.Request, fileLength);

 // apply the ranges to the download info
 downloadInfo.InitializeRanges(requestedRanges);

 string etagMatched;
 int outcomeStausCode = 200;

 // validating the ranges specified
 if (!HeadersParser.ValidatePartialRequest(context.Request, downloadInfo, out etagMatched, ref outcomeStausCode))
 {
  // the request is invalid, this is the invalid code
  context.Response.StatusCode = outcomeStausCode;

  // show to the client what is the real ETag
  if (!string.IsNullOrEmpty(etagMatched))
   context.Response.AppendHeader("ETag", etagMatched);

  // stop the preoccess
  // but don't hassle with error messages
  return;
 }

 // user ID, or IP or anything you use to identify the user
 var userIP = context.Request.UserHostAddress;

 // limiting the download speed manager and the speed limit
 UserSpeedLimitManager.StartNewDownload(downloadInfo, userIP, DownloadLimit);

 // It is very important to destory the DownloadProcess object
 // Here the using block does it for us.
 using (var process = new DownloadProcess(downloadInfo))
 {
  // start the download
  var state = process.ProcessDownload(context.Response);

  // checking the state of the download
  if (state == DownloadProcess.DownloadProcessState.PartFinished)
  {
   // all parts of download are finish, do something here!
  }
 }
}

 
  • دانلود
جهت دانلود از این آدرس و یا این آدرس اقدام کنید.
کدهای این پروژه به صورت آنلاین در این آدرس در دسترس هستند.

چندین کانکشن همزمان برای دانلود در حالی که کاربر به 50KB/s محدود شده است

  • مشکلات احتمالی
یک مشکل که فعلا حل نشده است و مربوط به محدودیت دانلود اعمال شده است که ممکن است دقیق اعمال نشود, برای مثال اگر محدودیت 50KB/s را اعمال کرده باشید سرعت دانلود کاربر بین 30 تا 60 در نوسان خواهد بود.

  • کدهای استفاده شده از سایر نویسندگان
کلاس ThrottledStream جهت اعمال محدودیت در پهنای باند مصرفی. اینجا.
نسخه ابتدایی و قدیمی ZipHandler در زبان vb.net که چند سال پیش این ماژول ها بر اساس آن تبدیل و تهیه شد. اینجا.

 

3 بازخورد برای “ماژول دانلودر برای ASP.NET”

  1. بسیار عالی و کاربردی.ممنون/

    پاسخحذف
  2. سلام استاد عزیز و محترم
    من مدتهاست که دنبال این کلاس بودم و از آنجا که در مورد استفاده از کلاس های مشابه به مشکلات زیادی برخوردم اگر اجازه بفرمایید در مورد این Handler به بحث و گفتگو بنشینیم تا تمامی مشکلات احتمالی در مورد آن را مرتفع کنیم.

    سوال 1: چرا Handler؟
    آیا برای استفاده از Handler دلیل خاصی وجود دارد؟
    میشه به صورت تخصصی در این زمینه توضیح بدید؟
    آیا نمیشد همچون گذشته از یک کلاس برای دانلود استفاده کرد؟
    آیا استفاده از Handler در مورد دانلود دارای مزیت خاصی است؟

    سوال 2 : آیا این کلاس فایل های بزرگ به طور مثال 5 گیگ را ساپورت می کند؟

    سوال 3:آیا بخش بخش دانلود کردن قسمت های مختلف فایل توسط IDM توسط ما باید مدیریت شود یا توسط خود IDM این قضیه Handle می گردد؟
    اگر توسط ماست کدام قسمت کد این کار را انجام می دهد و چگونه ؟

    سوال 4:در گذشته از Handler ی دیگر با کدی متفاوت برای دانلود فایل استفاده می کردم که در ابتدا به ظاهر همه چیش ok بود.
    اما پس از اینکه یک شرط کوچک رو در اول Handler اضافه می کردم (مثلا ولید بودن کاربر) قابلیت MultiThreading در IDM ازبین می رفت و واقعاً این موضوع برایم کلافه کننده شده بود؟
    آیا چنین شرطی رو میشه به این Handler اضافه نمود؟

    سوال 5:در کلاس های گذشته مشکلی که وجود داشت این بود که گاهی کاربران تماس می گرفتند و می گفتند فایل ناقص دانلود می شود و Error crc می دهد.
    آیا چنین مشکلی در مورد این Handler وجود ندارد؟
    دلیل بوجود آمدن این ارور چه بود؟

    با تشکر از حسن توجه و رسیدگی شما
    همچنین تشکر مجدد برای به روز رسانی این کلاس توسط شما که به جرات می توان گفت نمونه به روز آن وجود ندارد

    پاسخحذف
  3. سلام, شما لطف دارید.
    1- Handler دسترسی سطح پایینی را بدون سربار اضافه در اختیار قرار می ده. و چون دانلود هم نیاز به کار اضافی ندارد بهترین جا برای انجام عملیات دانلود همین جاست.
    در ضمن این ماژول به Handler محدود نیستید, شعی مورد نیاز HttpContext است که در همه درخواستهای وب در دسترسی است.

    2- اساسا این کلاس برای کار با فایلهای بزرگ طراحی شده چون در فایلهای کوچک این قابلیت ها کاربرد چندانی ندارد.
    نکته مهم در اینجا مربوط تنظیم صحیح مدت زمان مجاز برای اجرای درخواستهای کاربر هست که توسط asp.net کنترل می شه. مقدار آن به طور پیش فرض 110 ثانیه است. مقدارش رو افزایش بدین تا مشکلی پیش نیاد. مقدار executionTimeout اینجا:
    http://msdn.microsoft.com/en-us/library/e1f13641.aspx
    همچنین جهت جلوگیری از سرباز اضافه حتما محدودیت پهنای باند رو اعمال کنید

    3- توسط نرم افزار دانلودر کنترل می شود.

    4- کد خودتون رو قبل از استفاده از این کلاسهای بنویسید. تنها کاری که نباید انجام دهید این است که اگر کاربر رو برای دانلود هدایت می کنید نباید چیزی در خروجی بنویسید و یا تغییری اعمال کنید. دانلودر کار خودش رو درست انجام می ده.

    5- این همان چیزی است که در مورد 2 اشاره شد, با تنظیم زیاد مقدار executionTimeout موردی نباید پیش بیاد.
    همچنین بهتره که executionTimeout رو فقط برای صفحه handler زیاد کنید (با استفاده از کانفیگ location).

    پاسخحذف