[转载]【译】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); } } }
另请参阅