Asp实现MVC处理⽂件的上传下载功能实例教程
上传于下载功能是程序设计中⾮常常见的⼀个功能,在ASP.NET程序开发中有着⾮常⼴泛的应⽤。本⽂就以实例形式来实现这⼀功能。
⼀、概述
如果你仅仅只有Asp Web Forms背景转⽽学习Asp MVC的,我想你的第⼀个经历或许是那些曾经让你的编程变得愉悦⽆⽐的服务端控件都驾鹤西去了.FileUpload就是其中⼀个,⽽这个控件的缺席给我们带来⼀些⼩问题。这篇⽂章主要说如何在Asp MVC中上传⽂件,然后如何再从服务器中把上传过的⽂件下载下来.
⼆、实现⽅法
1.⽂件上传
在Web Forms中,当你把⼀个FileUpload控件拖到设计器中,你或许没有注意到在⽣成的HTML中会在form标签中加⼊⼀条额外属性enctype="multipart/form-data". ⽽FileUpload控件本⾝会⽣成为<input type=”file” />,在MVC的view⾥,有许多种⽅法可以做到同样效果,第⼀种的HTML如下:
<form action="/" method="post" enctype="multipart/form-data">
<input type="file" name="FileUpload1" /><br />
<input type="submit" name="Submit" id="Submit" value="Upload" />
</form>
注意form标签已经包括了enctype标签,⽽method属性则设为”post”,这样设置并不多于因为默认的提交时通过HTTP get⽅式进⾏的。下⾯这种⽅式,使⽤Html.BeginForm()扩展⽅法,会⽣成和上⾯同样的HTML:
<%
using (Html.BeginForm("", "home", FormMethod.Post, new {enctype="multipart/form-data"}))
{%>
<input type="file" name="FileUpload1" /><br />
<input type="submit" name="Submit" id="Submit" value="Upload" />
<% }%>
注意<input type=”file”>标签的name属性,我们在后⾯再讨论
OK,现在我们可以浏览本地⽂件然后通过Upload提交按钮将⽂件提交到服务器端,下⼀步就是在服务器端处理上传的⽂件,在使⽤fileUpload控件时,你可以很轻松的通过FileUpload的hasFile⽅法来查看⽂件是否被上传。但是在Asp MVC中貌似就不是这么⽅便了,你会和原始的HTTP更接近⼀些,然⽽,⼀个扩展⽅法可以处理这些:
public static bool HasFile(this HttpPostedFileBase file)
{
return (file != null && file.ContentLength > 0) ? true : false;
}
当你看到对应的Controller类的代码时,你会发现Request对象作为HttpRequestBase类型的⼀个属性存在。HttpReuqestBase 其实是HTTP请求的⼀个封装,暴漏了很多属性,包括Files collection(其实是HttpFileCollectionBase的集合),在集合中的每⼀个元素都是HttpPostedFileBase的集合,扩展⽅法是⽤于确保上传的⽂件是否存在。实际上,这和FileUpload.HasFile()⽅法的⼯作原理⼀致。
connect下载在Controller Action中使⽤起来其实很容易:
public class HomeController : Controller
{
public ActionResult Index()
{
foreach (string upload in Request.Files)
{
if (!Request.Files[upload].HasFile()) continue;
string path = AppDomain.CurrentDomain.BaseDirectory + "uploads/";
string filename = Path.GetFileName(Request.Files[upload].FileName);
Request.Files[upload].SaveAs(Path.Combine(path, filename));
}
return View();
}
}
2.多⽂件上传
或许你已经⽐我更早的想到如何更好的将Request.Files作为⼀个集合使⽤。这意味着它不仅仅只能容纳⼀个⽂件,⽽能容纳多个,我们将上⾯的View改为如下:
<%
using (Html.BeginForm("", "home", FormMethod.Post, new {enctype="multipart/form-data"}))
{%>
<input type="file" name="FileUpload1" /><br />
<input type="file" name="FileUpload2" /><br />
<input type="file" name="FileUpload3" /><br />
<input type="file" name="FileUpload4" /><br />
<input type="file" name="FileUpload5" /><br />
<input type="submit" name="Submit" id="Submit" value="Upload" />
<% }%>
在Controller的代码中已经检查了是否所有的⽂件上传框中都有⽂件,所以即使对于多⽂件上传,我们也不再需要修改Controller的代码,注意每⼀个<input type=”file”>都有不同的name属性,如果你需要调⽤其中⼀个,⽐如说,你需要引⽤第三个输⼊框只需要使⽤:Request.Files["FileUpload3"].
3.存⼊数据库
在你冲我狂吼”关注点分离”之前,我想声明下⾯的代码仅仅⽤于作为说明功能.我将ADO.Net的代码放⼊Controller action中,但我们都知道,这并不好。数据访问的代码应该放在Model中某个部分的数据访问层中.但是,下⾯这段代码仅仅可以给⼤家怎样将上传的⽂件存⼊数据库中⼀个更直观的印象,⾸先,我们需要创建⼀个数据表(FileTest)并创建⼀个表:FileStore
CREATE TABLE [dbo].[FileStore](
[ID] [int] IDENTITY(1,1) NOT NULL,
[FileContent] [image] NOT NULL,
[MimeType] [nvarchar](50) NOT NULL,
[FileName] [nvarchar](50) NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
FileContent域是image数据类型,⽤于存储以⼆进制数据形成的⽂件,⽽Index Action改为:
public ActionResult Index()
{
foreach (string upload in Request.Files)
{
if (!Request.Files[upload].HasFile()) continue;
string mimeType = Request.Files[upload].ContentType;
Stream fileStream = Request.Files[upload].InputStream;
string fileName = Path.GetFileName(Request.Files[upload].FileName);
int fileLength = Request.Files[upload].ContentLength;
byte[] fileData = new byte[fileLength];
fileStream.Read(fileData, 0, fileLength);
const string connect = @"Server=.\SQLExpress;Database=FileTest;Trusted_Connection=True;";
using (var conn = new SqlConnection(connect))
{
var qry = "INSERT INTO FileStore (FileContent, MimeType, FileName) VALUES (@FileContent, @MimeType, @FileName)";
var cmd = new SqlCommand(qry, conn);
cmd.Parameters.AddWithValue("@FileContent", fileData);
cmd.Parameters.AddWithValue("@MimeType", mimeType);
cmd.Parameters.AddWithValue("@FileName", fileName);
conn.Open();
cmd.ExecuteNonQuery();
}
}
return View();
}
修改后的代码会以循环的⽅式遍历Web页⾯中所有的上传⽂件,并检查<input type=”file”>中是否已经加⼊⽂件,然后,从⽂件中提取出3个信息:⽂件名,MIME类型(⽂件的类型),HTTP Request中的⼆进制流。⼆进制数据被转换为byte数组,并以image数据类型存⼊数据库。MIME类型和⽂件名对于⽤户从数
据库中提取⽂件来说⾮常重要。
4.将数据库中的⽂件返回给⽤户:
你如何将⽂件传送给⽤户取决于你最开始如何存储它,如果你将⽂件存⼊数据库,你会⽤流的⽅式将⽂件返还给⽤户,如果你将⽂件存在硬盘中,你只需要提供⼀个超链接即可,或者也可以以流的⽅式。每当你需要以流的⽅式将⽂件送到浏览器中,你都的使⽤到File()⽅法的重载(⽽不是使⽤我们先前⼀直使⽤的View()⽅法),对于File()⽅法有3类返回类型:FilePathResult,FileContentResult和FileStreamResult,第⼀种类型⽤于直接从磁盘返回⽂件;第⼆种类型⽤于将byte数组返回客户端;⽽第三种⽅式将已经⽣成并打开的流对象的内容返回客户端。
如果你还记得的话,我们将上传的⽂件存⼊了数据库,并以byte数组的形式存⼊FileContent域内.⽽当需要提取时,它仍然会以⼀个byte数组进⾏提取,这意味着我们使⽤返回FileContentResult的File()重载,如果我们想让提取的⽂件名更有意义,我们使⽤接受3个参数的重载,三个参数是:byte数组,MIME类型,⽂件名:
public FileContentResult GetFile(int id)
{
SqlDataReader rdr; byte[] fileContent = null;
string mimeType = "";string fileName = "";
const string connect = @"Server=.\SQLExpress;Database=FileTest;Trusted_Connection=True;";
using (var conn = new SqlConnection(connect))
{
var qry = "SELECT FileContent, MimeType, FileName FROM FileStore WHERE ID = @ID";
var cmd = new SqlCommand(qry, conn);
cmd.Parameters.AddWithValue("@ID", id);
conn.Open();
rdr = cmd.ExecuteReader();
if (rdr.HasRows)
{
rdr.Read();
fileContent = (byte[])rdr["FileContent"];
mimeType = rdr["MimeType"].ToString();
fileName = rdr["FileName"].ToString();
}
}
return File(fileContent, mimeType, fileName);
}
在View中最简单的使⽤来使⽤这个Action只需提供⼀个超链接:
<a href="/GetFile/1">Click to get file</a>
如果在数据库中存储的图⽚是图⽚类型,和使⽤超链接不同的是,我们通过指向Controller action的⼀个带有src属性的
<image>标签来获取:
<img src="/GetFile/1" alt="My Image" />
下⾯再让我们来看看使⽤FilePathResult(⽤于从硬盘提取⽂件)是多简单的事:
public FilePathResult GetFileFromDisk()
{
string path = AppDomain.CurrentDomain.BaseDirectory + "uploads/";
string fileName = "";
return File(path + fileName, "text/plain", "");
}
⽽这也可以⽤过超链接提取:
<a href="/GetFileFromDisk">Click to get file</a>
⽽最后⼀个选择FileStreamResult也可以从磁盘中提取⽂件:
public FileStreamResult StreamFileFromDisk()
{
string path = AppDomain.CurrentDomain.BaseDirectory + "uploads/";
string fileName = "";
return File(new FileStream(path + fileName, FileMode.Open), "text/plain", fileName);
}
三、补充
FilePathResult和FileStreamResult的区别是什么?我们⼜该如何取舍呢?主要的区别是FilePathResult使⽤HttpResponse.TransmitFile来将⽂件写⼊Http输出流。这个⽅法并不会在服务器内存中进⾏缓冲,所以这对于发送⼤⽂件是⼀个不错的选择。他们的区别很像DataReader和DataSet的区别。于此同时,
TransmitFile还有⼀个bug,这可能导致⽂件传到客户端⼀半就停了,甚⾄⽆法传送。⽽FileStreamResult在这⽅⾯就很棒了。⽐如说:返回Asp Chart 控件在内存中⽣成的图表图⽚,⽽这并不需要将图⽚存到磁盘中.

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。