【譯】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);
}
}
}
另請參閱

浙公網安備 33010602011771號