zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    文件编程---库函数
    linux文件编程----系统调用
    makefile工程管理
    快速上手日期插件laydate
    js判断浏览器类型以及语言
    谷歌支付服务端详细讲解(PHP)
    php中的date和strtotime函数妙用
    本地搭建GitLab
    mysql查询语句常用字段操作函数
    php中签名公钥、私钥(SHA1withRSA签名)以及AES(AES/ECB/PKCS5Padding)加密解密详解
  • 原文地址:https://www.cnblogs.com/gisbeginner/p/5116682.html
Copyright © 2011-2022 走看看