2014年10月7日 星期二

[iThome 第七屆鐵人賽 15] 處理檔案上傳

在網站裡面,通常都會需要讓使用者上傳檔案,好方便前臺或者別的顯示這個資訊的地方來下載這個對應的檔案。

在Mvc裡面,有所謂的HttpPostedFileBase可以方便我們接到前端input是file的檔案。這個檔案通常會被存到Server的某一個位置之後,路勁才會儲存到DB 裡面,下次顯示的時候顯示的是這個檔案的路徑。

處理HttpPostedFileBase的邏輯其實還滿常見,如果框架能夠把這一部份也處理掉的話,又可以減少我們煩惱這些細節的部份,提升開發效率。

這一篇我們將來看一下如何做到。

整體功能概念

我們的Entity欄位通常會是string的形態,用來儲存這個上傳檔案的相對路徑。而檔案上傳方便做model binding是HttpPosedFileBase這個形態,因此我們將會在ViewModel增加一個對應的property用來接使用者所選取的檔案。

我們的邏輯處理規則是需要:

  1. HttpPostedFileBase的檔案存到特定的位置。
  2. 把儲存的檔案相對的路徑存入到正確的欄位裡面。

功能實作

有了上面整體的概念之後,將會開始實作。

欄位說明

假設我們現在的Post需要有一個欄位用來儲存這篇文章的代表圖,因此我們會多一個欄位叫做CoverImg(在ViewModel),同樣對應到DB的Table欄位也是 CoverImg

我們會在多一個欄位叫做CoverImgFile在ViewModel,這個欄位的主要目的是對應到View裡面的HttpPostedFileBase

因此目前Post的定義會是(標亮是新增的兩個欄位):

public partial class Create
{
public int Id { get; set; }
public string Title { get; set; }
public string PostContent { get; set; }
public System.DateTime CreateDateTime { get; set; }
public Nullable<System.DateTime> LastModifyDateTime { get; set; }
public string CoverImg { get; set; }
public HttpPostedFileBase CoverImgFile { get; set; }
}

View的說明


再來,對應的View會變成:

@if(string.IsNullOrEmpty(Model.CoverImg) == false)
{
<img src="~/@Model.CoverImg" />
}

<input type="file" name="CoverImgFile" />

首先是如果檔案已經有上傳過(表示CoverImg有值),就以圖片方式顯示。


然後CoverImgFile則是實際的檔案上傳。


檔案上傳的處理


Create的地方將會處理檔案上傳並且把路徑存到CoverImg

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Create post)
{
if(post.CoverImgFile != null)
{
var fileName = DateTime.Now.ToString();

post.CoverImgFile.SaveAs(fileName);

post.CoverImg = fileName;
}

if (ModelState.IsValid)
{
service.CreateViewModelToDatabase(post);
return RedirectToAction("Index");
}

return View(post);
}

這邊不管是否有驗證成功,都會把檔案儲存起來,避免Validation錯誤返回Model的時候,之前選的檔案會不存在。


結語


到這裡之後,相信對於如何處理檔案上傳和把檔案路徑放入對應的欄位已經瞭解。


但是,相信使用上面來說不是很方便。首先,寫在Controller裡面這部份的邏輯就很不適合,再來,如果這個邏輯以後要修改或者需要通用基本上做不到。


因此,在下一篇,將會介紹如何透過Service層,把處理檔案上傳的邏輯抽出來,讓這部份的邏輯能夠共用。


沒有留言 :

張貼留言