تغییرات متدهای بازگشت فایل‌ها به سمت کلاینت در ASP.NET Core
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: سه دقیقه

اگر خروجی return File را در اکشن متدهای ASP.NET Core همانند ASP.NET MVC 5.x مورد استفاده قرار دهید و در آن مسیرکامل فایل را برای بازگشت قید کرده باشید، پیام یافت نشدن فایل را دریافت خواهید کرد؛ هرچند این فایل بر روی سرور و در مسیر ذکر شده وجود خارجی دارد. علت آن‌را در تصویر ذیل می‌توانید مشاهده کنید:



روش‌های مختلف بازگشت فایل‌ها به سمت کلاینت در ASP.NET Core

در ASP.NET Core، نوع‌های کاملتری از Action Result‌های مرتبط با بازگشت فایل‌ها تدارک دیده شده‌اند که نحوه‌ی طراحی آن‌ها را در شکل فوق ملاحظه می‌کنید. در اینجا FileResult والد تمام حالت‌های بازگشت فایل است که شامل موارد ذیل می‌شود:
FileContentResult: از آن برای بازگشت آرایه‌ای از بایت‌ها استفاده می‌شود:
//returns the file content as an array of bytes
public FileContentResult FileContentActionResult()
{
   var file = System.IO.File.ReadAllBytes(@"C:\path\dir1\HomeController.cs");
   return File(file, "text/plain", "HomeController.cs");
}
زمانیکه Controller جاری از کلاس پایه Controller ارث بری می‌کند، متد File در این کلاس پایه قرار دارد. به همین جهت مانند مثال فوق به سادگی می‌توان به آن، بدون ذکر new دسترسی یافت. روش دیگر دسترسی به FileContentResult به صورت ذیل است که معادل قطعه کد فوق می‌باشد:
public IActionResult TestFileContentActionResult()
{
   var file = System.IO.File.ReadAllBytes(@"C:\path\dir1\HomeController.cs");
   return new FileContentResult(file, "text/plain") { FileDownloadName = "HomeController.cs" };
}

FileStreamResult: این Action Result قابلیت Streaming بازگشت فایل‌ها را مهیا می‌کند:
//return the file as a stream
public FileStreamResult FileStreamActionResult()
{
   //var file = System.IO.File.ReadAllBytes(@"C:\path\dir1\HomeController.cs");
   //var stream = new MemoryStream(file, writable:true);

   var fileStream = new FileStream(@"C:\path\dir1\HomeController.cs", FileMode.Open, FileAccess.Read);
   return File(fileStream, "text/plain", "HomeController.cs");
}
در اینجا برای مثال می‌توان یک MemoryStream و یا یک FileStream را به سمت کاربر ارسال کرد. این روش نسبت به خواندن فایل‌ها در آرایه‌ای از بایت‌ها و سپس ارسال یکجای آن، بهینه‌تر است و حافظه‌ی کمتری را مصرف می‌کند.
اگر خواستیم مستقیما با FileStreamResult کار کنیم، روش کار به صورت ذیل است:
public IActionResult TestFileStreamActionResult()
{
   //var file = System.IO.File.ReadAllBytes(@"C:\path\dir1\HomeController.cs");
   //var stream = new MemoryStream(file, writable:true);
   var fileStream = new FileStream(@"C:\path\dir1\HomeController.cs", FileMode.Open, FileAccess.Read);
   return new FileStreamResult(fileStream, "text/plain") { FileDownloadName = "HomeController.cs" };
}

VirtualFileResult: در این مورد آدرسی را که ارائه می‌دهید، باید به فایلی درون پوشه‌ی wwwroot اشاره کند (علت اصلی بروز مشکلی که در مقدمه‌ی بحث عنوان شد). در اینجا آدرس کامل فایل مدنظر نیست.
//returns a file specified with a virtual path
public VirtualFileResult VirtualFileActionResult()
{
   return File("/css/site.css", "text/plain", "site.css");
}
و یا معادل همین قطعه کد با استفاده از VirtualFileResult اصلی به صورت ذیل است:
public IActionResult TestVirtualFileActionResult()
{
   return new VirtualFileResult("/css/site.css", "text/plain") { FileDownloadName = "site.css" };
}

PhysicalFileResult: اگر قصد دارید آدرس کامل فایلی را مشخص کنید (بجای مسیر نسبی آن که از wwwroot شروع می‌شود؛ مانند حالت قبل)، اینبار باید از متد PhysicalFile استفاده کرد:
//returns the specified file on disk, that is it's physical address
public PhysicalFileResult PhysicalFileActionResult()
{
   return PhysicalFile(@"C:\path\dir1\HomeController.cs", "text/plain", "HomeController.cs");
}
این قطعه کد نیز بر اساس استفاده‌ی مستقیم از PhysicalFileResult شکل زیر را می‌تواند پیدا کند:
public IActionResult TestPhysicalFileActionResult()
{
   return new PhysicalFileResult(@"C:\path\dir1\HomeController.cs", "text/plain")
   {
      FileDownloadName = "HomeController.cs"
   };
}

در این متدها و کلاس‌ها، اگر FileDownloadName حاوی حروف اسکی نباشد، به صورت خودکار encoding از نوع RFC5987 بر روی آن اعمال خواهد شد.
  • #
    ‫۶ سال و ۳ ماه قبل، جمعه ۴ خرداد ۱۳۹۷، ساعت ۱۹:۳۷
    سلام.
    اگر بخوام همین دانلود فایل از یک آدرس دیگه انجام بشه باید چه کرد؟ مثلا سایت بنده هست mySite.Com میخوام روی دکمه کلیک شد از آدرس dl.MySite.Com دانلود بشه اما این آدرس رو نبینه و قابلیت ریزام رو هم داشته باشه.
    • #
      ‫۶ سال و ۳ ماه قبل، شنبه ۵ خرداد ۱۳۹۷، ساعت ۰۱:۵۲
      زیردامنه dl روی یک هاست دیگر هست یا روی همین هاست هست؟ آیا به صورت فیزیکی به آن دسترسی دارید؟ اگر بله، مطالبی که در اینجا عنوان شده کافی هست و نیازی به هیچ نوع پیاده سازی خاصی ندارد، چون از سرگیری مجدد جزئی از خواص متدهای بازگشت فایل توسط ASP.NET Core است. اگر خیر، در اینجا باید فایل را دریافت کنید. در مطلب «از سرگیری مجدد، لغو درخواست و سعی مجدد دریافت فایل‌های حجیم توسط HttpClient» این مورد بحث شده‌است. قابلیت resume، همان از سرگیری مجدد هست. یعنی مدیریت range headers. کلاینت هدر range را با مقداری مانند bytes=500-999 ارسال می‌کند. شما این ابتدا و انتها را دریافت می‌کنید و سپس توسط روشی که در آن مطلب عنوان شده، مقدار RangeHeaderValue را تعیین و دریافت می‌کنید. البته من تابحال ندیدم کسی چنین کاری را انجام دهد، چون پهنای باند مصرفی یک سرور را دو برابر می‌کند!