ASP.NET MVC 使用 Ajax Helper 搭配 jQuery Ajax 實作非同步式圖片上傳與刪除。
說明
Controller
- Album
- GetPhoto (retun partial)
- AddPhoto (Post-only)
- DeletePhoto (Post-only)
View
- Album
- GetPhoto (Partial)
Model
使用 App_Data 當中建立 mdf 的方式建立本次專案,並且搭配 SSMS 直接於 (localdb)\MSSQLLocalDB 進行資料庫的設計,僅有一張簡單的資料表負責圖片的上傳作業。
CREATE TABLE [dbo].[Photo](
[Id] [int] IDENTITY(1,1) PRIMARY KEY NOT NULL,
[FileName] [nvarchar](100) NOT NULL,
[UpdateDateTime] [datetime2](7) NOT NULL,
[MIME] [varchar](50) NOT NULL,
[Path] [nvarchar](200) NOT NULL
)
Controller
Album
相簿的主頁,但 Controller 的工作卻相當簡單,只是負責回應 View。
public ActionResult Album()
{
return View();
}
GetPhoto
Ajax 如果達成的靈魂 Action,負責提供 Partial View,提供 Ajax 呼叫所需的 Html 以進行頁面的更新。
public ActionResult GetPhoto()
{
return PartialView(db.Photo.ToList());
}
AddPhoto
負責將收到的 HttpPostedFileBase 進行實體檔案的加入以及資料庫的內容寫入,因為是示範專案,許多上傳檔案需要進行的安全檢查沒有加入。
而回應的 ActionResult 為導向 GetPhoto,重新渲染 Photo。這樣雖然有利用到 Ajax 但還是處理了許多不需要更動的 HTML DOM ,在效能考量上不是最理想,精神上是參考 Ajax Helper Replace 的設計方式,好處就是設計上直覺且便利。
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddPhoto(HttpPostedFileBase file)
{
string extension = Path.GetExtension(file.FileName);
string fileName = $"{Guid.NewGuid()}{extension}";
string savePath =
Path.Combine(Server.MapPath("~/UploadFile"), fileName);
file.SaveAs(savePath);
db.Photo.Add(
new Photo
{
FileName = fileName,
Path = savePath,
UpdateDateTime = DateTime.Now,
MIME = MimeMapping.GetMimeMapping(savePath)
}
);
db.SaveChanges();
return RedirectToAction("GetPhoto", "Home");
}
DeletePhoto
public ActionResult DeletePhoto(int id)
{
var photo = db.Photo.Find(id);
if (photo != null)
{
if (System.IO.File.Exists(photo.Path))
{
System.IO.File.Delete(photo.Path);
}
db.Photo.Remove(photo);
db.SaveChanges();
}
return RedirectToAction("GetPhoto", "Home");
}
View
解決 Bootstrap 3 Column Height 不一致問題
解決方法就是在 Row 加入 Flex,可以使用 inline-css:
<div class="row" style="display: flex; flex-wrap: wrap">
<div class="col-md-2">
<img src="..." class="img-responsive">
</div>
</div>
元素較多的時候可以改採 CSS:
.equalHeight {
display: flex;
flex-wrap: wrap
}
Album.cshtml
核心內容為 #album 的 Container 用於將 Partial View 鑲嵌於此
<div id="album">
@Html.Action("GetPhoto")
</div>
搭配 Ajax ActionLink,可以用於非同步式地更新圖片顯示,本次範例中沒有使用到,但是使用 Ajax BeginForm 的前身,也方便在測試使用:
<div style="display: inline-block">
@Ajax.ActionLink(
"Update Photo", "GetPhoto", "Home", new { id = 0 },
new AjaxOptions
{
HttpMethod = "GET",
UpdateTargetId = "album",
InsertionMode = InsertionMode.Replace
},
new { @class = "btn btn-primary", style = "margin-right: 5px" }
)
</div>
實際使用的是 Ajax BeginForm,用於將上傳的圖片以 Form 的形式提供給 Home/AddPhoto Action
<div style="display: inline-block">
@using (Ajax.BeginForm("AddPhoto", "Home", new { id = 0 },
new AjaxOptions()
{
HttpMethod = "POST",
UpdateTargetId = "album",
InsertionMode = InsertionMode.Replace
}, new { enctype = "multipart/form-data" }))
{
@Html.AntiForgeryToken()
<input type="file" name="file" style="display: inline-block">
<input type="submit"
value="Upload File to Server"
style="display: inline-block"
class="btn btn-primary">
}
</div>
Script Section 有兩大功能:
第一個是負責將 jquery.unobtrusive-ajax.js 函式庫載入,以使用 Ajax。
第二個功能則是負責在 document 註冊 click 事件,用於處理圖片被點擊後刪除的事件。
@section scripts{
@Scripts.Render("~/Scripts/jquery.unobtrusive-ajax.min.js")
<script>
$(document).on('click', '.removePhoto', function (e) {
$.ajax("/Home/DeletePhoto/" + this.id, {
success: function (data) {
$('#album').html(data);
}
});
console.log(this.id);
});
</script>
}
因為使用 Ajax 的方式將 getPhoto 的結果取代 Html Dom,如果不是在 Document 上使用 on 來註冊 Listener 的話,Listener 會因為 Dom 被更新而消失。如下列的程式碼的錯誤示範。
下列為錯誤示範,只能夠執行一次,後續會因 Listener 消失而無法再次使用。
$('.removePhoto').click(function (e) {
$.ajax("/Home/DeletePhoto/" + this.id, {
success: function (data) {
$('#album').html(data);
}
});
console.log(this.id);
});
GetPhoto.cshtml
GetPhoto 的任務單純,就是負責渲染 Model 當中的圖片,有使用到 flex style 解決 Bootstrap 3 Column Height 不一致的問題。
@model IList<MiniAlbum.Models.Photo>
<div class="row" style="margin-top:25px; display: flex; flex-wrap: wrap">
@foreach (var img in Model)
{
<div class="col-md-2">
<a class="removePhoto" id="@img.Id">
<img src="~/UploadFile/@img.FileName" class="img-responsive" />
</a>
</div>
}
</div>
小結
本專案僅為練習 ASP.NET MVC 搭配 jQuery Unobtrusive Ajax,在小型的專案可以採用,以提升更佳的網站使用體驗。但如果要追求更理想的體驗與更複雜的應用情境,還是得仰賴前端框架使用,並以 WebAPI 分前、後端的方式進行開發。
而本專案對於圖片的上傳、刪除還有可以改進的地方,例如刪除前的警示,圖片的上傳與刪除安全性上的考量也需加入,以免衍生資安漏洞。