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