asp.net mvc 上传图片 摘自mvc 高级编程第311页
Image Uploads
I am going to complete the SportsStore user experience with something a little more sophisticated: I will add the ability for the
administrator to upload product images and store them in the database so that they are displayed in the product catalog. This isn't
something that is especially interesting or useful in its own right, but it does allow me to demonstrate some important MVC
Framework features.
Extending the Database
Open the Visual Studio Server Explorer window and navigate to the Products table in the database created in
Chapter 7. The name of the data connection may have changed to be EFDbContext, which is the name assigned to the
connection in the Web.config file. Visual Studio is a little bit inconsistent about when it renames the connection, so you
might also see the original name that was shown when the connection was created. Right-click on the Products table and
select New Query from the pop-up menu and enter the following SQL into the text area:
ALTER TABLE [dbo].[Products]
ADD [ImageData] VARBINARY (MAX) NULL,
[ImageMimeType] VARCHAR (50) NULL;
Click the Execute button (which is marked with an arrow) in the top-left cover of the window and Visual Studio will update
the database, adding two new columns to the table. To test the update, right-click on the Products table in the Server
Explorer window and select Open Table Definition from the menu. You will see that there are now columns
called ImageData and ImageMimeType, as shown in Figure 12-3.
Tip If the columns are not visible, close the design window, right-click on the data connection in the Server
Explorer window and select Refresh from the menu. The new columns should now be visible when you select the
Open Table Definition menu item again.
Enhancing the Domain Model
I need to add two new fields to the Products class in the SportsStore.Domain project that correspond to the new
database columns, as shown in Listing 12-10.
Listing 12-10. Adding Properties in the Product.cs File
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace SportsStore.Domain.Entities {
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; }
public string ImageMimeType { get; set; }
}
}
Caution Make sure that the names of the properties that you add to the Product class exactly match the names you gave
to the new columns in the database.
Creating the Upload User Interface Elements
The next step is to add support for handling file uploads. This involves creating a UI that the administrator can use to upload an
image. Modify the Views/Admin/Edit.cshtml view so that it matches Listing 12-11.
Listing 12-11. Adding Support for Images in the Edit.cshtml File
@model SportsStore.Domain.Entities.Product
@{
312
ViewBag.Title = "Admin: Edit " + @Model.Name;
Layout = "∼/Views/Shared/_AdminLayout.cshtml";
}
<div class="panel">
<div class="panel-heading">
<h3>Edit @Model.Name</h3>
</div>
@using (Html.BeginForm("Edit", "Admin",
FormMethod.Post, new { enctype = "multipart/form-data" })) {
<div class="panel-body">
@Html.HiddenFor(m => m.ProductID)
@foreach (var property in ViewData.ModelMetadata.Properties) {
switch (property.PropertyName) {
case "ProductID":
case "ImageData":
case "ImageMimeType":
// do nothing
break;
default:
<div class="form-group">
<label>@(property.DisplayName ??
property.PropertyName)</label>
@if (property.PropertyName == "Description") {
@Html.TextArea(property.PropertyName, null,
new { @class = "form-control", rows = 5 })
} else {
@Html.TextBox(property.PropertyName, null,
new { @class = "form-control" })
}
@Html.ValidationMessage(property.PropertyName)
</div>
break;
}
}
<div class="form-group">
<div style="position:relative;">
<label>Image</label>
<a class='btn' href='javascript:;'>
Choose File...
<input type="file" name="Image" size="40"
style="position:absolute;z-index:2;top:0;
left:0;filter: alpha(opacity=0); opacity:0;
backgroundcolor:
transparent;color:transparent;"
onchange='$("#upload-fileinfo").
html($(this).val());'>
</a>
<span class='label label-info' id="upload-file-info">
</span>
</div>
@if (Model.ImageData == null) {
313
<div class="form-control-static">No Image</div>
} else {
<img class="img-thumbnail" width="150" height="150"
src="@Url.Action("GetImage", "Product",
new { Model.ProductID })" />
}
</div>
</div>
<div class="panel-footer">
<input type="submit" value="Save" class="btn btn-primary" />
@Html.ActionLink("Cancel and return to List", "Index", null, new {
@class = "btn btn-default"
})
</div>
}<
/div>
You may already be aware that Web browsers will upload files properly only when the HTML form element defines an
enctype value of multipart/form-data. In other words, for a successful upload, the form element must look
like this:
...
<form action="/Admin/Edit" enctype="multipart/form-data" method="post">
...
Without the enctype attribute, the browser will transmit only the name of the file and not its content, which is no use at all.
To ensure that the enctype attribute appears, I must use an overload of the Html.BeginForm helper method that lets
me specify HTML attributes, like this:
...
@using (Html.BeginForm("Edit", "Admin",
FormMethod.Post, new { enctype = "multipart/form-data" })) {
...
There are two other changes in the view. The first is that I have replaced the Razor if expression I used when generating
input elements with a switch statement. The effect is the same, but it allows me to specify the model properties I want to
skip more concisely, and I don’t want to display the image-related properties directly to the user.
Instead, I have made the remaining change, which is to add an input element whose type is file to allow file upload, along
with an img element to display the image associated with a product, if there is one in the database.
The horrific mess of inline CSS and JavaScript addresses a shortcoming in the Bootstrap library: it does not properly style file
input elements. There are a number of extensions that add the missing functionality, but I have chosen the magic incantation
shown in the listing because it is self-contained and is reliable. It doesn’t change the way that the MVC Framework works, just the
way in which the elements in the Edit.cshtml file are styled.
Saving Images to the Database
I need to enhance the POST version of the Edit action method in the Admin controller so that I take the image data that has
been uploaded and save it in the database. Listing 12-12 shows the changes that are required.
Listing 12-12. Handling Image Data in the AdminController.cs File
using System.Linq;
using System.Web;
using System.Web.Mvc;
using SportsStore.Domain.Abstract;
314
using SportsStore.Domain.Entities;
namespace SportsStore.WebUI.Controllers {
[Authorize]
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
= null) {
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");
}
315
}
}
I have added a new parameter to the Edit method, which the MVC Framework uses to pass the uploaded file data to the
action method. I check to see if the parameter value is null; if it is not, I copy the data and the MIME type from the parameter
to the Product object so that it is saved to the database. I must also update the EFProductRepository class in the
SportsStore.Domain project to ensure that the values assigned to the ImageData and ImageMimeType
properties are stored in the database. Listing 12-13 shows the required changes to the SaveProduct method.
Listing 12-13. Ensuring That the Image Values Are Stored in the Database in the EFProductRepository.cs File
...
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();
}.
..
Implementing the GetImage Action Method
In Listing 12-11, I added an img element whose content was obtained through a GetImage action method on the
Product controller. I am going to implement this action method so that I can display images contained in the database. Listing
12-14 shows the definition of the action method.
Listing 12-14. The GetImage Action Method in the ProductController.cs File
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using SportsStore.Domain.Abstract;
using SportsStore.Domain.Entities;
using SportsStore.WebUI.Models;
namespace SportsStore.WebUI.Controllers {
public class ProductController : Controller {
private IProductRepository repository;
public int PageSize = 4;
public ProductController(IProductRepository productRepository) {
316
this.repository = productRepository;
}
public ViewResult List(string category, int page = 1) {
ProductsListViewModel model = new ProductsListViewModel {
Products = repository.Products
.Where(p => category == null || p.Category ==
category)
.OrderBy(p => p.ProductID)
.Skip((page - 1) * PageSize)
.Take(PageSize),
PagingInfo = new PagingInfo {
CurrentPage = page,
ItemsPerPage = PageSize,
TotalItems = category == null ?
repository.Products.Count() :
repository.Products.Where(e => e.Category ==
category).Count()
},
CurrentCategory = category
};
return View(model);
}
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;
}
}
}
}
This method tries to find a product that matches the ID specified by the parameter. The FileContentResult class is
used as the result from an action method when you want to return a file to the client browser, and instances are created using the
File
asp.net mvc 上传图片 摘自mvc 高级编程第311页的更多相关文章
- 4、线程池(摘自C#高级编程第7版)
1.需求背景 创建线程需要时间.如果有不同的小任务完成,就可以事先创建许多线程,在应完成这些任务时发出请求.这个线程数最好在需要更多的线程时增加,在需要释放资源时减少. 2.线程池出场 不需要自己 ...
- 解读经典《C#高级编程》继承 页107-113.章4
前言 本章节开始讲接口. 接口 接口的声明很像抽象类,有方法的声明但无方法体.但它比抽象类限制更多.和类比起来,接口的定义有众多限制. 接口只能包含声明,而无方法体 接口只能包含方法.属性.索引器.事 ...
- 解读经典《C#高级编程》泛型 页114-122.章4
前言 本章节开始讲解泛型..Net从2.0开始支持泛型,泛型不仅是C#的一部分,也与IL代码紧密集成.所以C#中泛型的实现非常优雅.相对于C#,Java是后期引入的泛型,受限于最初的设计架构,就实现的 ...
- .Net高级编程-自定义错误页 web.config中<customErrors>节点配置
错误页 1.当页面发生错误的时候,ASP.Net会将错误信息展示出来(Sqlconnection的错误就能暴露连接字符串),这样一来不好看,二来泄露网站的内部实现信息,给网站带来安全隐患,因此需要定制 ...
- 解读经典《C#高级编程》泛型 页122-127.章4
前言 本篇继续讲解泛型.上一篇讲解了泛型类的创建.本篇讲解泛型类创建和使用的细节. 泛型类 上篇举了个我产品中用到的例子,本篇的功能可以对照着此案例进行理解. /// <summary> ...
- 【C#高级编程(学习与理解)】1.1 C#与.NET的关系
1.C#语言不能孤立使用,而必须和.NET Framework一起考虑.C#编译器专门用于.NET,这表示用C#编写的所有代码总是在.NET Framework中运行. 2.C#就其本身而言只是一种语 ...
- ASP.NET MVC 4高级编程(第4版)
<ASP.NET MVC 4高级编程(第4版)> 基本信息 作者: (美)Jon Galloway Phil Haack Brad Wilson K. Scott All ...
- C# 6 与 .NET Core 1.0 高级编程 - 41 ASP.NET MVC(上)
译文,个人原创,转载请注明出处(C# 6 与 .NET Core 1.0 高级编程 - 41 ASP.NET MVC(上)),不对的地方欢迎指出与交流. 章节出自<Professional C# ...
- Asp.net mvc 4.0 高级编程 百度云下载
Asp.net mvc 4.0 高级编程 百度云下载地址:链接:http://pan.baidu.com/s/1o6zFvOe 密码:xyss 1.基于 ASP.NET MVC4.0 + WebAPI ...
随机推荐
- SPM——How to use github
In this semester, we take a class called 'Software Project Management'. And in this class, we have l ...
- OpenMP 《并行程序设计导论》的补充代码
▶ 使用 OpenMP 和队列数据结构,在各线程之间传递信息 ● 代码,使用 critical 子句和 atomic 指令来进行读写保护 // queue.h #ifndef _QUEUE_H_ #d ...
- Mock.js开发中拦截Ajax
Mock.js 是一款前端开发中拦截Ajax请求再生成随机数据响应的工具.可以用来模拟服务器响应. 优点是非常简单方便, 无侵入性, 基本覆盖常用的接口数据类型. 在我们的生产实际中,后端的接口往往是 ...
- css常用属性初总结:font
平时在做项目时,UX常说的一句话就是“这里的字体不对吧,字体大小也不太对,你们前端有没有按规范来”,今天,我们就一起来看看这折磨人的font属性. 字体属性font-family 允许值 系列名 初始 ...
- NGUI中显示DrawCall详细信息
[NGUI显示DrawCall详细信息] UIDrawCall中有个宏,SHOW_HIDDEN_OBJECTS,默认为关闭状态.将此宏打开,NGUI即会将DrawCall对象显示在Hierarchy中 ...
- 11-SSH综合案例:前台用户模块:激活邮件的发送
刚才已经把服务器的环境和客户端的软件已经搭建好了,现在就要发送邮件了.现在发送邮件的代码你不用重点去掌握啊,了解一下就行了. javax.activation javax.mail是Java EE 5 ...
- Python_01-入门基础
以后我会发表一系列python脚本的学习资料,python版本为2.x. 目录: 1 Python入门基础 1.1 学习资源 1.2 所有语言的入门程序---Hello World! 1.3 帮助函 ...
- SuSE 网卡配置模板
heidsoft:/etc/sysconfig/network # cat ifcfg.template ## This is a template for a network interface c ...
- send anywhere真的好用啊
手机和电脑互传文件,方便很多.
- CiteSpace安装使用简介
一.简介 CiteSpaceⅡ基于JAVA平台的信息可视化软,是美国Drexel大学陈超美(Chaomei Chen)教授开发的,用于文献引文网络分析的信息,作为文献计量学方面最先进的分析工具之一,是 ...