Upload و Download فایل در ASP.Net MVC

اگر شما با پس زمینه Asp.Net Web Form وارد Asp.Net MVC شوید ، اولین چیزهایی که به ذهنتان خواهد رسید این است که همه کنترل های سرور آسان ناپدید شده اند . یکی از آنها فایل آپلود است . به نظر می آید که این کمبود ها باعث بروز مشکلاتی می شود . در این مقاله قصد داریم نشان دهیم که چگونه می توان به سرور در MVC فایل آپلود کرد .

Upload  و Download  فایل در  ASP.Net MVC

اگر شما با پس زمینه Asp.Net Web Form  وارد Asp.Net MVC   شوید ، اولین چیزهایی که به ذهنتان خواهد رسید این است که همه کنترل های سرور آسان ناپدید شده اند .  یکی از آنها فایل آپلود است . به نظر می آید که این کمبود ها باعث بروز مشکلاتی می شود .  در این مقاله قصد داریم نشان دهیم که چگونه می توان به سرور در MVC   فایل آپلود کرد .

در وب فرم هنگامی که یک کنترل فایل آپلود در صفحه طراحی درج می کردیم ، هنگامی که صفحه اجرا می شود اتفاقاتی روی می دهد که شما متوجه آنها نمی شوید. فرم HTML نتیجه ، کاری انجام داده اند که کل صفحه را با ویژگی های اضافی تزئین میکند: enctype="multipart/form-data" ) ) ،فایل آپلود خودش را به عنوان HTML  ارائه می دهد . input type=file)) . در میان یک View  در  MVC چند راه برای تنظیم فایل آپلود وجود دارد . راه اول با استفاده از HTML   : 

<form action="/" method="post" enctype="multipart/form-data">
  <input type="file" name="FileUpload1" /><br />
  <input type="submit" name="Submit" id="Submit" value="Upload" />
</form>

توجه داشته باشید که تگ <form>  شامل صفت enctype  می شود و صفت متد Post  است . زیرا  فرم به صورت پیش فرض با متد Get  ارسال می شود .  روش زیر با استفاده از  ، extension method  (Html.BeginForm() ارائه شده است و زمانی که صفحه درخواست داده می شود نتیجه کاملا شبیه HTML  است .

@using (Html.BeginForm("", "home", FormMethod.Post, new {enctype="multipart/form-data"})){ 

     <input type="file" name="FileUpload1" /><br />
     <input type="submit" name="Submit" id="Submit" value="Upload" />
}

توجه داشته باشید که نام گذاری صفت صحیح باشد : <input type="file">

صفحه نتیجه به صورت زیر خواهد بود .


اکنون می توانیم فایل خود را به وب سرور آپلود کنیم .  آیتم دیگری نیز برای مدیریت فایل های آپلود شده روی وب سرویس نیاز است . زمانی که از کنترل فایل آپلود استفاده میکنید ، شما به طور کلی کدی مشاهده میکنید که چک میکند اگر یک فایل آپلود شده است از FileUpload.HasFile() متد استفاده شود . اما زمانی که با MVC  کار میکنید به این راحتی نیست زیرا که شما به HTTP  خام نزدیک تر هستید . گرچه یک extension method به صورت زیر کار را آسان می کند .

public static bool HasFile(this HttpPostedFileBase file)
{
  return (file != null && file.ContentLength > 0) ? true : false;
}

اگر به کلاس کنترولر نگاه کنید ، مشاهده می کنید که یک شی درخواستی به عنوان یک ویژگی دارد که نوع آن HttpRequestBase است. این یک حالت بسته بندی برای درخواست HTTP  است ، و در معرض ویژگی های زیادی قرار دارد ، شامل یک Files collection (در واقع یک مجموعه از نوع HttpFileCollectionBase) . هر موردی در مجموعه از نوع HttpPostedFileBase است . extension method ، نوع آیتم ها و محتوای آنها را چک میکند. این یک راه است که از روش متد FileUpload.HasFile() کار میکند .

کد زیر را در قسمت  Action  کنترلر قرار دهید :

public class HomeController : Controller
{
  public ActionResult Index()
  {
    foreach (string upload in Request.Files)
    {
      if (!Request.Files[upload].HasFile()) continue;
      string path = AppDomain.CurrentDomain.BaseDirectory + "uploads/";
      string filename = Path.GetFileName(Request.Files[upload].FileName);
      Request.Files[upload].SaveAs(Path.Combine(path, filename));
    }
    return View();
  }
}

فایل آپلود چندگانه

فایل ها یک مجموعه هستند و این نشان می دهد که می توانند بیش از یک فایل در خود جای دهند. View  اصلی را به صورت زیر تغییر دهید .

@using (Html.BeginForm("", "home", FormMethod.Post, new {enctype="multipart/form-data"})){
     <input type="file" name="FileUpload1" /><br />
     <input type="file" name="FileUpload2" /><br />
     <input type="file" name="FileUpload3" /><br />
     <input type="file" name="FileUpload4" /><br />
     <input type="file" name="FileUpload5" /><br />
     <input type="submit" name="Submit" id="Submit" value="Upload" />
}

خروجی به صورت زیر می باشد :

در واقع کد قسمت کنترلر همه فایل آپلودها را چک میکند، پس تغییری برای کار کردن فایل آپلود چند گانه لازم نیست. توجه داشته باشید که هر ورودی صفت نام متفاوتی دارد.  

بعد از ساختن فایل آپلود نیاز داریم تا فایل های آپلود شده در جایی نگه داری شوند . ابتدا یک دیتابیس با نام  FileTest  ایجاد میکنیم. و یک جدول با نام FileStore  به آن اضافه میکنیم .

CREATE TABLE [dbo].[FileStore](
[ID] [int] IDENTITY(1,1) NOT NULL,
[FileContent] [image] NOT NULL,
[MimeType] [nvarchar](50) NOT NULL,
[FileName] [nvarchar](50) NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

فیلد  FileContent  از نوع  Image  می باشد.  

 Index  به صورت زیر تغییر میکند.

public ActionResult Index()
{
  foreach (string upload in Request.Files)
  {
    if (!Request.Files[upload].HasFile()) continue;

    string mimeType = Request.Files[upload].ContentType;
    Stream fileStream = Request.Files[upload].InputStream;
    string fileName = Path.GetFileName(Request.Files[upload].FileName);
    int fileLength = Request.Files[upload].ContentLength;
    byte[] fileData = new byte[fileLength];
    fileStream.Read(fileData, 0, fileLength);

    const string connect = @"Server=.\SQLExpress;Database=FileTest;Trusted_Connection=True;";
    using (var conn = new SqlConnection(connect))
    {
      var qry = "INSERT INTO FileStore (FileContent, MimeType, FileName) VALUES (@FileContent, @MimeType, @FileName)";
      var cmd = new SqlCommand(qry, conn);
      cmd.Parameters.AddWithValue("@FileContent", fileData);
      cmd.Parameters.AddWithValue("@MimeType", mimeType);
      cmd.Parameters.AddWithValue("@FileName", fileName);
      conn.Open();
      cmd.ExecuteNonQuery();
    }
  }
  return View();
}

کد اصلاح شده آپلودهای زیادی را روی وب سرویس انجام می دهد و چک میکند که هر کدام از آنها دارای یک فایل باشند. از اینجا ، 3 قسمت از اطلاعات گسترش پیدا میکند : نام فایل  ،   mime type نوع فایل، داده های باینری در جریان درخواست  HTTP   . داده های باینری به یک آرایه منتقل می شوند ، که همان چیزی است که در زمینه نوع داده تصویر در پایگاه داده ذخیره می شود. Mime type  و نام مهم هستند برای زمانی که فایل به یک کاربر بازگردانده می شود.

خدمات فایل به کاربر

ارائه فایل به کاربر در درجه اول به چگونگی ذخیره آن بستگی دارد. اگر آنها را در دیتابیس ذخیره کرده اید، شما می توانید طی یک جریان معمولی فایل را به کاربر برگردانید . اگر روی دیسک ذخیره کرده اید ، به سادگی می توانید از یک  hyperlink  استفاده کنید یا دوباره آنها را به جریان بیاندازید. هر زمان که شما نیاز به جریان انداختن یک فایل داشتید می توانید از یکی از Overload های متد  File()  استفاده کنید.  سه راه مختلف برای برگرداندن انواع از متد فایل وجود دارد: FilePathResultFileContentResultFileStreamResult . در جریان اول یک فایل مستقیما از دیسک برگردانده می شود ، در راه دوم یک آرایه به کلاینت برگردانده می شود، در راه سوم محتوای یک جریان که تولید و باز شده برگردانده می شود .

هنگامی که فایل های آپلود شده را در یک دیتابیس ذخیره کردیم ، یک آرایه به فیلد  FileContent  فرستادیم. زمانی که نیاز داریم تا آن راپس بگیریم باز هم به صورت یک آرایه است . اگر می خواهید آن را نگه دارید می توانید از دو روش از  File() استفاده کنید که یک FileContentResult برمیگرداند. اگر میخواهید که نام فایل پر معنا باشد باید از سرباری استفاده کنید که 3 نشانوند byte array و mime type و file name استفاده کنید:

public FileContentResult GetFile(int id)
{
  SqlDataReader rdr; byte[] fileContent = null; 
  string mimeType = "";string fileName = "";
  const string connect = @"Server=.\SQLExpress;Database=FileTest;Trusted_Connection=True;";

  using (var conn = new SqlConnection(connect))
  {
    var qry = "SELECT FileContent, MimeType, FileName FROM FileStore WHERE ID = @ID";
    var cmd = new SqlCommand(qry, conn);
    cmd.Parameters.AddWithValue("@ID", id);
    conn.Open();
    rdr = cmd.ExecuteReader();
    if (rdr.HasRows)
    {
      rdr.Read();
      fileContent = (byte[])rdr["FileContent"];
      mimeType = rdr["MimeType"].ToString();
      fileName = rdr["FileName"].ToString();
    }
  }
  return File(fileContent, mimeType, fileName);
}

آسان ترین راه فراخوانی این متد تهیه hyperlink است :

<a href="/GetFile/1">Click to get file</a>

اگر فایل ذخیره شده در دیتابیس تصویر است بجای هایپر لینک در کنترلر در بین  src  از یک  تگ <img>  استفاده کنید .

<img src="/GetFile/1" alt="My Image" />

ببینیم که چگونه از FilePathResult استفاده کنیم . این روش برای استفاده از فایل مستقیما از روی دیسک استفاده می شود .

public FilePathResult GetFileFromDisk()
{
  string path = AppDomain.CurrentDomain.BaseDirectory + "uploads/";
  string fileName = "test.txt";
  return File(path + fileName, "text/plain", "test.txt");
}

و این برای فراخوانی یک هایپرلینک ساده استفاده می شود .

<a href="/GetFileFromDisk">Click to get file</a>

گزینه آخر FileStreamResult برای خدمات فایل از روی دیسک استفاده می شود .

public FileStreamResult StreamFileFromDisk()
{
  string path = AppDomain.CurrentDomain.BaseDirectory + "uploads/";
  string fileName = "test.txt";
  return File(new FileStream(path + fileName, FileMode.Open), "text/plain", fileName);
}

تفاوت بین FilePathResult  و FileStreamResult  چیست  و از کدام باید استفاده کرد؟ مهم ترین تفاوت آنها این است که FilePathResult از HttpResponse.TransmitFile  برای نوشتن فایل روی خروجی  HTTPاستفاده می کند . این متد فایل ها را در حافظه روی سرور بافر نمیکند ، پس میتواند گزینه بهتری برای فرستادن فایل های بزرگ باشد. FileStreamResult یک راه بسیار خوبی است، برای مثال، بازگشت تصاویر نمودار تولید شده در حافظه توسط نمودار کنترل ASP.NET بدون نیاز به آنکه آنها را بر روی دیسک ذخیره کنید.

 

فایل های ضمیمه