標籤:
這裡示範如何在MVC WEB應用程式如何上傳圖片到資料庫以及如何在WEB頁面上顯示圖片。資料庫表對應整個Model類,不單圖片資料一個欄位,我們從資料表的定義開始:
CREATE TABLE [dbo].[Products] ( [ProductID] INT IDENTITY (1, 1) NOT NULL, [Name] NVARCHAR (MAX) NOT NULL, [Description] NVARCHAR (MAX) NOT NULL, [Price] DECIMAL (18, 2) NOT NULL, [Category] NVARCHAR (MAX) NOT NULL, [ImageData] VARBINARY (MAX) NULL, [ImageMimeType] NVARCHAR (MAX) NULL, CONSTRAINT [PK_dbo.Products] PRIMARY KEY CLUSTERED ([ProductID] ASC));
儲存圖片的欄位為ImageData,類型VARBINARY,欄位ImageMimeType儲存圖片的類型。我們使用Entity framework處理c#對象到資料庫的操作,我們不需要編寫代碼通過SQL代碼操作資料庫,Entity framework負責資料庫的操作。Entity framework支援Model first和Code-first兩種方式,Code-first先編寫c# model類,然後綁定到資料庫,相關的內容可以參見http://msdn.microsoft.com/en-us/data/jj200620。
從Model類開始:
public class Product { [HiddenInput(DisplayValue = false)] public int ProductID { get; set; } [Required(ErrorMessage = "Please enter a product name")] public string Name { get; set; } [DataType(DataType.MultilineText)] [Required(ErrorMessage = "Please enter a description")] public string Description { get; set; } [Required] [Range(0.01, double.MaxValue, ErrorMessage = "Please enter a positive price")] public decimal Price { get; set; } [Required(ErrorMessage = "Please specify a category")] public string Category { get; set; } public byte[] ImageData { get; set; } [HiddenInput(DisplayValue = false)] public string ImageMimeType { get; set; } }
Entity framework可以在VS中使用nuget package manager安裝,安裝完entity framework我們建立Entity framework的會話類將Model和資料庫聯絡起來:
using System.Data.Entity;namespace SportsStore.Domain.Concrete { public class EFDbContext : DbContext { public DbSet<Product> Products { get; set; } }}
我們還需要告訴Entity framework使用哪個資料庫,在web.config添加相應的連接字串:
...<connectionStrings> <add name="EFDbContext" connectionString="Data Source=(localdb)\v11.0;Initial Catalog=SportsStore;Integrated Security=True" providerName="System.Data.SqlClient"/> </connectionStrings>...
接下來建立一個使用EFDbContext操作Model的輔助類:
public interface IProductRepository { IQueryable<Product> Products { get; } void SaveProduct(Product product); Product DeleteProduct(int productID); }public class EFProductRepository : IProductRepository { private EFDbContext context = new EFDbContext(); public IQueryable<Product> Products { get { return context.Products; } } public void SaveProduct(Product product) { if (product.ProductID == 0) { context.Products.Add(product); } else { Product dbEntry = context.Products.Find(product.ProductID); if (dbEntry != null) { dbEntry.Name = product.Name; dbEntry.Description = product.Description; dbEntry.Price = product.Price; dbEntry.Category = product.Category; dbEntry.ImageData = product.ImageData; dbEntry.ImageMimeType = product.ImageMimeType; } } context.SaveChanges(); } public Product DeleteProduct(int productID) { Product dbEntry = context.Products.Find(productID); if (dbEntry != null) { context.Products.Remove(dbEntry); context.SaveChanges(); } return dbEntry; } }
定義IProductRepository介面是方便後續使用Dependency injection從介面擷取實作類別EFProductRepository的執行個體,這裡就不列出具體如何?。EFProductRepository使用EFDbContext完成添加、儲存對象到資料庫以及從資料庫刪除對象。完成資料模型的操作,下面定義控制器的方法:
public class AdminController : Controller { private IProductRepository repository; public AdminController(IProductRepository repo) { repository = repo; } public ViewResult Index() { return View(repository.Products); } public ViewResult Edit(int productId) { Product product = repository.Products .FirstOrDefault(p => p.ProductID == productId); return View(product); } [HttpPost] public ActionResult Edit(Product product, HttpPostedFileBase image) { if (ModelState.IsValid) { if (image != null) { product.ImageMimeType = image.ContentType; product.ImageData = new byte[image.ContentLength]; image.InputStream.Read(product.ImageData, 0, image.ContentLength); } repository.SaveProduct(product); TempData["message"] = string.Format("{0} has been saved", product.Name); return RedirectToAction("Index"); } else { // there is something wrong with the data values return View(product); } } public ViewResult Create() { return View("Edit", new Product()); } [HttpPost] public ActionResult Delete(int productId) { Product deletedProduct = repository.DeleteProduct(productId); if (deletedProduct != null) { TempData["message"] = string.Format("{0} was deleted", deletedProduct.Name); } return RedirectToAction("Index"); } }
這裡兩個Edit ation,第一個顯示編輯上傳頁面,帶HttpPost特性的用於處理編輯頁面提交回傳,提交的image資料為HttpPostedFileBase類型,我們從中取出影像檔的資料儲存到Model類的ImageData屬性,ContentType則記錄到ImageMimeType屬性。對應的Edit視圖:
@model SportsStore.Domain.Entities.Product@{ ViewBag.Title = "Admin: Edit " + @Model.Name; Layout = "~/Views/Shared/_AdminLayout.cshtml";}<h1>Edit @Model.Name</h1>@using (Html.BeginForm("Edit", "Admin", FormMethod.Post, new { enctype = "multipart/form-data" })) { @Html.EditorForModel() <div class="editor-label">Image</div> <div class="editor-field"> @if (Model.ImageData == null) { @:None } else { <img width="150" height="150" src="@Url.Action("GetImage", "Product", new { Model.ProductID })" /> } <div>Upload new image: <input type="file" name="Image" /></div> </div> <input type="submit" value="Save" /> @Html.ActionLink("Cancel and return to List", "Index")}
這裡指定表單的enctype=multipart/form-data,缺少這個屬性工作表單提交的資料中會只有圖片檔案的名稱而不包含圖片檔案的資料。圖片顯示img單元的src指向一個從product的action產生的網址,我們還需要實現這個方法:
... public FileContentResult GetImage(int productId) { Product prod = repository.Products.FirstOrDefault(p => p.ProductID == productId); if (prod != null) { return File(prod.ImageData, prod.ImageMimeType); } else { return null; } } ...
這裡從圖片檔案資料和mime類型返回一個FileContentResult。
這就是實現上傳圖片到資料庫的完整過程,實際的應用中我們還需要限制檔案大小,通過檔案尾碼名或者ContentType檢查是否是有效圖片檔案。
以上內容摘自《Apress Pro ASP.NET MVC 4》第四版,詳見原版 http://www.apress.com/9781430242369。
ASP.NET MVC 4 - 上傳圖片到資料庫