Asp.net实现MVC处理文件的上传下载删除功能实例教程

摘要:
上传和下载功能是程序设计中非常常见的功能,在ASP.NET程序开发中有着非常广泛的应用。本文以示例的形式实现了此功能。但在Asp.netMVC中,它似乎并不那么方便。您将更接近原始HTTP。然而,扩展方法可以处理这些问题:?

上传于下载功能是程序设计中非常常见的一个功能,在ASP.NET程序开发中有着非常广泛的应用。本文就以实例形式来实现这一功能。

一、概述

如果你仅仅只有Asp.net Web Forms背景转而学习Asp.net MVC的,我想你的第一个经历或许是那些曾经让你的编程变得愉悦无比的服务端控件都驾鹤西去了.FileUpload就是其中一个,而这个控件的缺席给我们带来一些小问题。这篇文章主要说如何在Asp.net MVC中上传文件,然后如何再从服务器中把上传过的文件下载下来.

二、实现方法

1.文件上传

在Web Forms中,当你把一个FileUpload控件拖到设计器中,你或许没有注意到在生成的HTML中会在form标签中加入一条额外属性enctype="multipart/form-data". 而FileUpload控件本身会生成为<input type=”file” />,在MVC的view里,有许多种方法可以做到同样效果,第一种的HTML如下:

1
2
3
4
<formaction="/"method="post"enctype="multipart/form-data"
 <inputtype="file"name="FileUpload1"/><br/> 
 <inputtype="submit"name="Submit"id="Submit"value="Upload"/> 
</form>

注意form标签已经包括了enctype标签,而method属性则设为”post”,这样设置并不多于因为默认的提交时通过HTTP get方式进行的。下面这种方式,使用Html.BeginForm()扩展方法,会生成和上面同样的HTML:

1
2
3
4
5
6
<% 
 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.net MVC中貌似就不是这么方便了,你会和原始的HTTP更接近一些,然而,一个扩展方法可以处理这些:

1
2
3
4
publicstaticboolHasFile(thisHttpPostedFileBase file) 
 return(file != null&& file.ContentLength > 0) ? true: false
}

当你看到对应的Controller类的代码时,你会发现Request对象作为HttpRequestBase类型的一个属性存在。HttpReuqestBase其实是HTTP请求的一个封装,暴漏了很多属性,包括Files collection(其实是HttpFileCollectionBase的集合),在集合中的每一个元素都是HttpPostedFileBase的集合,扩展方法是用于确保上传的文件是否存在。实际上,这和FileUpload.HasFile()方法的工作原理一致。

在Controller Action中使用起来其实很容易:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
publicclassHomeController : Controller 
 publicActionResult Index() 
 
  foreach(stringupload inRequest.Files) 
  
   if(!Request.Files[upload].HasFile()) continue
   stringpath = AppDomain.CurrentDomain.BaseDirectory + "uploads/"
   stringfilename = Path.GetFileName(Request.Files[upload].FileName); 
   Request.Files[upload].SaveAs(Path.Combine(path, filename)); 
  
  returnView(); 
 
}

2.多文件上传

或许你已经比我更早的想到如何更好的将Request.Files作为一个集合使用。这意味着它不仅仅只能容纳一个文件,而能容纳多个,我们将上面的View改为如下:

1
2
3
4
5
6
7
8
9
10
<% 
 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

1
2
3
4
5
6
CREATETABLE[dbo].[FileStore]( 
[ID] [int] IDENTITY(1,1) NOTNULL
[FileContent] [image] NOTNULL
[MimeType] [nvarchar](50) NOTNULL
[FileName] [nvarchar](50) NOTNULL 
) ON[PRIMARY] TEXTIMAGE_ON [PRIMARY]

FileContent域是image数据类型,用于存储以二进制数据形成的文件,而Index Action改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
publicActionResult Index() 
 foreach(stringupload inRequest.Files) 
 
  if(!Request.Files[upload].HasFile()) continue
  stringmimeType = Request.Files[upload].ContentType; 
  Stream fileStream = Request.Files[upload].InputStream; 
  stringfileName = Path.GetFileName(Request.Files[upload].FileName); 
  intfileLength = Request.Files[upload].ContentLength; 
  byte[] fileData = newbyte[fileLength]; 
  fileStream.Read(fileData, 0, fileLength); 
  conststringconnect = @"Server=.SQLExpress;Database=FileTest;Trusted_Connection=True;"
  using(var conn = newSqlConnection(connect)) 
  
   var qry = "INSERT INTO FileStore (FileContent, MimeType, FileName) VALUES (@FileContent, @MimeType, @FileName)"
   var cmd = newSqlCommand(qry, conn); 
   cmd.Parameters.AddWithValue("@FileContent", fileData); 
   cmd.Parameters.AddWithValue("@MimeType", mimeType); 
   cmd.Parameters.AddWithValue("@FileName", fileName); 
   conn.Open(); 
   cmd.ExecuteNonQuery(); 
  
 
 returnView(); 
}

修改后的代码会以循环的方式遍历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类型,文件名:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
publicFileContentResult GetFile(intid) 
 SqlDataReader rdr; byte[] fileContent = null;  
 stringmimeType = "";stringfileName = ""
 conststringconnect = @"Server=.SQLExpress;Database=FileTest;Trusted_Connection=True;"
 using(var conn = newSqlConnection(connect)) 
 
  var qry = "SELECT FileContent, MimeType, FileName FROM FileStore WHERE ID = @ID"
  var cmd = newSqlCommand(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(); 
  
 
 returnFile(fileContent, mimeType, fileName); 
}

在View中最简单的使用来使用这个Action只需提供一个超链接:

1
<ahref="/GetFile/1">Click to get file</a>

如果在数据库中存储的图片是图片类型,和使用超链接不同的是,我们通过指向Controller action的一个带有src属性的<image>标签来获取:

1
<imgsrc="/GetFile/1"alt="My Image"/>

下面再让我们来看看使用FilePathResult(用于从硬盘提取文件)是多简单的事:

1
2
3
4
5
6
publicFilePathResult GetFileFromDisk() 
 stringpath = AppDomain.CurrentDomain.BaseDirectory + "uploads/"
 stringfileName = "test.txt"
 returnFile(path + fileName, "text/plain", "test.txt"); 
}

而这也可以用过超链接提取:

1
<ahref="/GetFileFromDisk">Click to get file</a>

而最后一个选择FileStreamResult也可以从磁盘中提取文件:

1
2
3
4
5
6
publicFileStreamResult StreamFileFromDisk() 
 stringpath = AppDomain.CurrentDomain.BaseDirectory + "uploads/"
 stringfileName = "test.txt"
 returnFile(newFileStream(path + fileName, FileMode.Open), "text/plain", fileName); 
}

三、补充

FilePathResult和FileStreamResult的区别是什么?我们又该如何取舍呢?主要的区别是FilePathResult使用HttpResponse.TransmitFile来将文件写入Http输出流。这个方法并不会在服务器内存中进行缓冲,所以这对于发送大文件是一个不错的选择。他们的区别很像DataReader和DataSet的区别。于此同时, TransmitFile还有一个bug,这可能导致文件传到客户端一半就停了,甚至无法传送。而FileStreamResult在这方面就很棒了。比如说:返回Asp.net Chart 控件在内存中生成的图表图片,而这并不需要将图片存到磁盘中.

//删除文件
        string[] strpicname = Request["imgids"].Split(new char[] { ',' });
        for (int i = 0; i < strpicname.Length; i++)
        {
            File.Delete(Server.MapPath(strpicname[i].ToString()));
        }

免责声明:文章转载自《Asp.net实现MVC处理文件的上传下载删除功能实例教程》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇黑马lavarel教程---5、模型操作(AR模式)Linux下的sleep()和sched_yield()(转)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

随便看看

关于服务器并发量的简单计算

最简单的计算方式就是根据服务器带宽与页面的大小1.假设机房带宽为10Mbs,页面的大小为20KB同时并发量的理论值:10*1024/=64个请求/秒理论上1秒钟同时可以有64个请求访问页面。本考试系统,登陆的页面容量比较大,所有的js,css以及图片未优化前在400KB左右,我们就以400KB为基准,所有后面要用的文件是在首页一次性加载下来的。这一天的测评情...

RPi 树莓派 DSI 接口研究 MIPI raspberry pi

我已经玩树莓派很久了。我发现尚未使用DSI显示界面。经过一些研究,我发现它很有趣。我稍后会记录相关信息。(更新1:目前,整个网络上有很多方案来研究hdmi和mipi之间的相互转换方案:a.)mipi屏幕+hdmi界面:大多数都是因为有很多mipi屏幕和漂亮的参数而被研究的。详细信息:谷歌,得益于包括智汇在内的各种大神的研发,如Pocket LCD方案。最困难...

JavaScript算法学习:获取字符串最后一位方法及判断是否以指定字符串开始或结尾

Str.substr,其中start是必需的参数,表示坐标的起始位置。正值在正方向计数,负值在反方向计数,长度是可选参数,表示从起始位置开始计数的数字。...

docker安装MySQL5.7示例!!坑,ERROR 1045 (28000): Access denied for user

处理mysql1045错误1.在/usr/local/mysql/conf中添加一个文件。d目录:mysql文件的内容是:[mysqld]skip-grant-tables2重新启动mysql:dockerstartmysql5.73进入docker:dockerexec-itmysql5.7bash4登录mysql:mysql-uroot-p5将root密...

bootstrap删除模态框弹出并询问是否删除【通用删除模态框】

divclass=“模态对话框”&gt;divclass=“modal header”&gt;spanaria hidden=“true”&gt;h4class=“模态标题”&gt;divclass=“modal body”&gt;divclass=“模态页脚”&gt;...

springMVC使用map接收入参 + mybatis使用map 传入查询参数

测试示例:控制器层使用映射来接收请求参数。从Debug中可以看到,请求中的参数值都是字符串形式。如果接收参数的映射直接传输到服务,mybatis将在接收参数时报告错误。因此,您需要首先对请求中的参数1packageorg.slsale进行预处理。测验23导入java.util。日期4导入java.util。HashMap;5导入java.ut...