samesite


【译】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









posted @ 2011-11-30 22:35  技术弟弟  阅读(3237)  评论(3编辑  收藏  举报