[转载]ASP.NET MVC3 20个秘方-(11)通过表单上传文件

[转载]【译】MVC3 20个秘方-(11)通过表单上传文件 – 技术弟弟 – 博客园.

问题

你希望允许用户在你的网站上传并保存文件。

解决方案

通过HttpPostedFileBase.实现上传文件和保存到磁盘。

讨论

在接下来的例子里,之前创建的去添加和更新图书的View将被更新成允许用户选择一个文件并且上传缩略图文件。作为开始,Book/Create  view 应该被更新,改变From的编码类型并且为缩略图字段替换掉脚手架 textbox。代码如下:

@model MvcApplication.Models.Book
@{
    ViewBag.Title = "Create";
}
<h2>
    Create</h2>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")"
type="text/javascript"></script>
<script src="
@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"
type="text/javascript"></script>
@using (Html.BeginForm("Create", "Books", FormMethod.Post,
new { enctype = "multipart/form-data" }))
{
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>Book</legend>
        <div class="editor-label">
            @Html.LabelFor(model => model.Title)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Title)
            @Html.ValidationMessageFor(model => model.Title)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.Isbn)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Isbn)
            @Html.ValidationMessageFor(model => model.Isbn)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.Summary)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Summary)
            @Html.ValidationMessageFor(model => model.Summary)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.Author)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Author)
            @Html.ValidationMessageFor(model => model.Author)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.Thumbnail)
        </div>
        <div class="editor-field">
            <input type="file" name="file" />
            @Html.ValidationMessageFor(model => model.Thumbnail)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.Price)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Price)
            @Html.ValidationMessageFor(model => model.Price)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.Published)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Published)
            @Html.ValidationMessageFor(model => model.Published)
        </div>
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}
<div>
    @Html.ActionLink("Back to List", "Index")
</div>

Book/Edit view 也应该以相同的方式被更新,除了添加一个hidden字段(在旧的thumbnail那)。这将用于在BookController中上传新文件之前删除旧的文件。代码如下:

@model MvcApplication.Models.Book
@{
    ViewBag.Title = "Edit";
}
<h2>
    Edit</h2>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")"
type="text/javascript"></script>
<script src="
@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"
type="text/javascript"></script>
@using (Html.BeginForm("Edit", "Books", FormMethod.Post,
new { enctype = "multipart/form-data" }))
{
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>Book</legend>
        @Html.HiddenFor(model => model.ID)
        <div class="editor-label">
            @Html.LabelFor(model => model.Title)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Title)
            @Html.ValidationMessageFor(model => model.Title)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.Isbn)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Isbn)
            @Html.ValidationMessageFor(model => model.Isbn)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.Summary)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Summary)
            @Html.ValidationMessageFor(model => model.Summary)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.Author)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Author)
            @Html.ValidationMessageFor(model => model.Author)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.Thumbnail)
        </div>
        <div class="editor-field">
            <input type="file" name="file" />
            @Html.HiddenFor(model => model.Thumbnail)
            @Html.ValidationMessageFor(model => model.Thumbnail)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.Price)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Price)
            @Html.ValidationMessageFor(model => model.Price)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.Published)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Published)
            @Html.ValidationMessageFor(model => model.Published)
        </div>
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}
<div>
    @Html.ActionLink("Back to List", "Index")
</div>

由于BooksController中Create和edit功能都是保存上传文件,为了避免重复代码,我们将创建一个 新的类。这个类将被创建在Utils文件夹中。Utils文件夹->右击并选择添加→类。这类命名为FileUpload.cs。这个新的类将负责 两个关键功能:保存文件,并删除该文件。在下面的例子中,FileUpload类接收一个HttpPostedFile相应的变量,并将它保存到Web服 务器上的特定点。另一个功能呢,相反,它接收到的文件的名称,并从Web服务器删除它。

译者:下边标红的代码是我加上去的。这样我们可以把图片和缩略图存到我们项目的文件夹下。否则他会存到:C:\Program Files\Common Files\Microsoft Shared\DevServer\10.0\目录下。

代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
namespace MvcApplication.Utils
{
    public static class FileUpload
    {
        public static char DirSeparator = Path.DirectorySeparatorChar;
        public static string FilesPath = HttpContext.Current.Server.MapPath(string.Format("Content{0}Uploads{1}", DirSeparator, DirSeparator));
        public static string UploadFile(HttpPostedFileBase file)
        {
            // Check if we have a file
            if (null == file) return "";
            // Make sure the file has content
            if (!(file.ContentLength > 0)) return "";
            string fileName = file.FileName;
            string fileExt = Path.GetExtension(file.FileName);
            // Make sure we were able to determine a proper
            // extension
            if (null == fileExt) return "";
            // Check if the directory we are saving to exists
            if (!Directory.Exists(FilesPath))
            {
                // If it doesn't exist, create the directory
                Directory.CreateDirectory(FilesPath);
            }
            // Set our full path for saving
            string path = FilesPath + DirSeparator + fileName;
            // Save our file
            file.SaveAs(Path.GetFullPath(path));
            // Return the filename
            return fileName;
        }
        public static void DeleteFile(string fileName)
        {
            // Don't do anything if there is no name
            if (fileName.Length == 0) return;
            // Set our full path for deleting
            string path = FilesPath + DirSeparator + fileName;
            // Check if our file exists
            if (File.Exists(Path.GetFullPath(path)))
            {
                // Delete our file
                File.Delete(Path.GetFullPath(path));
            }
        }
    }
}

这里面的类和功能被定义为静态,以避免在BooksController中创建类的实例。在类的顶部,创建一个常数定义
文件将被保存在哪,这应该是需要去更新保存在您的网站上不同的位置。在UploadFile功能中,如果上传的文件目录已经不存在,它将使用System.IO.Directory类中的CreateDirectory函数去创建一个目录。在删除功能
中也有一个类似的检查改文件是否存在,存在的话使用File.Delete功能删除。如果检查不执行,将返回一个错误,“试图删除一个不存在的文件。”
最后BooksController需要更新。在下面的例子中,三个重要的变化:
1。更新Createaction去调用UploadFile功能。
2。更新Edit action,首先调用DeleteFile,然后调用UploadFile。
3。 更新Delete确认功能,在从数据库删除这本书之前调用DeleteFile

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcApplication.Models;
using PagedList;
using MvcApplication.Utils;

namespace MvcApplication.Controllers
{ 
    public class BookController : Controller
    {
        private BookDBContext db = new BookDBContext();

        //
        // GET: /Book/

        public ViewResult Index(string filter,int page = 1)
        {
            var books = from b in db.Books select b;
            #region Filter Switch
            switch (filter)
            {
                case "NewReleases":
                    var startDate = DateTime.Today.AddDays(-14);
                    books = books.Where(b => b.Published
                    <= DateTime.Today.Date
                    && b.Published >= startDate
                    );
                    break;
                case "ComingSoon":
                    books = books.Where(b => b.Published >
                    DateTime.Today.Date);
                    break;
                default:
                    // No filter needed
                    break;
            }
            ViewBag.CurrentFilter =
            String.IsNullOrEmpty(filter) ? "" : filter;
            #endregion
            books = books.OrderBy(b=>b.Author);
            const int maxRecords = 2;
            var currentPage = page <= 0 ? 1 : page;
            return View(books.ToPagedList(currentPage,
                                          maxRecords));
        }

        //
        // GET: /Book/Details/5

        public ViewResult Details(int id)
        {
            Book book = db.Books.Find(id);
            return View(book);
        }

        //
        // GET: /Book/Create

        public ActionResult Create()
        {
            return View();
        } 

        //
        // POST: /Book/Create

        [HttpPost]
        public ActionResult Create(Book book,HttpPostedFileBase file)
        {
            if (ModelState.IsValid)
            {
                // Upload our file
                book.Thumbnail = FileUpload.UploadFile(file);
                db.Books.Add(book);
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(book);

        }
        
        //
        // GET: /Book/Edit/5
 
        public ActionResult Edit(int id)
        {
            Book book = db.Books.Find(id);
            return View(book);
        }

        //
        // POST: /Book/Edit/5

        [HttpPost]
        public ActionResult Edit(Book book, HttpPostedFileBase file)
        {
            if (ModelState.IsValid)
            {
                // Delete old file
                FileUpload.DeleteFile(book.Thumbnail);
                // Upload our file
                book.Thumbnail = FileUpload.UploadFile(file);
                db.Entry(book).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(book);
        }

        //
        // GET: /Book/Delete/5
 
        public ActionResult Delete(int id)
        {
            Book book = db.Books.Find(id);
            return View(book);
        }

        //
        // POST: /Book/Delete/5

        [HttpPost, ActionName("Delete")]
        public ActionResult DeleteConfirmed(int id)
        {
            Book book = db.Books.Find(id);
            // Delete old file
            FileUpload.DeleteFile(book.Thumbnail);
            db.Books.Remove(book);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    }
}

另请参阅

HttpPostedFileBase

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏