利用Asp.net MVC处理文件的上传下载

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

在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.net 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()方法的工作原理一致。

在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();}
}

 

多文件上传

或许你已经比我更早的想到如何更好的将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"].

 

存入数据库

在你冲我狂吼”关注点分离”之前,我想声明下面的代码仅仅用于作为说明功能.我将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类型和文件名对于用户从数据库中提取文件来说非常重要。

将数据库中的文件返回给用户:

你如何将文件传送给用户取决于你最开始如何存储它,如果你将文件存入数据库,你会用流的方式将文件返还给用户,如果你将文件存在硬盘中,你只需要提供一个超链接即可,或者也可以以流的方式。每当你需要以流的方式将文件送到浏览器中,你都的使用到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 = "test.txt";return File(path + fileName, "text/plain", "test.txt");
}

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

<a href="/GetFileFromDisk">Click to get file</a>

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

public FileStreamResult StreamFileFromDisk()
{string path = AppDomain.CurrentDomain.BaseDirectory + "uploads/";string fileName = "test.txt";return File(new FileStream(path + fileName, FileMode.Open), "text/plain", fileName);
}

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

------------------------------------------------

原文链接:http://www.mikesdotnetting.com/Article/125/ASP.NET-MVC-Uploading-and-Downloading-Files

 

Translated by:CareySon

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/547003.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python遍历文件夹下所有文件及目录

遍历文件夹中的所有子文件夹及子文件使用os.walk()方法非常简单。 语法格式大致如下&#xff1a; os.walk(top[, topdownTrue[, onerrorNone[, followlinksFalse]]]) top – 根目录下的每一个文件夹(包含它自己), 产生3-元组 (dirpath, dirnames, filenames)【文件夹路径, 文…

你必须知道的session与cookie

Session本质 提到Session我们能联想到的就是用户登录功能&#xff0c;而本身我们使用Session的基础是通过url进行访问的&#xff0c;也就是使用http协议进行访问的&#xff0c;而http协议本身是无状态的&#xff0c;那么问题来了服务器端是怎么验证客户端身份的&#xff1f; …

华为交换机telnet和ftp服务开启/关闭命令

1.telnet开启/关闭 在系统视图下 启用方式如下&#xff1a; telnet server enable //使能telnet服务关闭方式如下&#xff1a; undo telnet server //关闭telnet服务2.FTP开启/关闭 通过display ftp-server查看启用状态 如果已经启用&#xff0c;会在查看的命令中显示&#…

为什么用Spring来管理Hibernate?

为什么用Spring来管理Hibernate&#xff1f;为什么要用Hibernate框架&#xff1f;这个在《Hibernate介绍》博客中已经提到了。既然用Hibernate框架访问管理持久层&#xff0c;那为何又提到用Spring来管理以及整合Hibernate呢&#xff1f;首先我们来看一下Hibernate进行操作的步…

Python-Pandas之两个Dataframe的差异比较

昨天在外网找到一个比较dataframe的好库&#xff0c;叫datacompy&#xff0c;它的优点有&#xff1a; 1、可以把对比后的信息详情打印出来&#xff0c;比如列是否相等&#xff0c;行是否相等&#xff1b; 2、在数据中如果有不相等列&#xff0c;那么就只比较相同的列&#xf…

《JavaScript权威指南》——JavaScript核心

前言 这本由David Flanagan著作&#xff0c;并由淘宝前端团队译的《JavaScript权威指南》&#xff0c;也就是我们俗称的“犀牛书”&#xff0c;算是JS界公认的“圣经”了。本书较厚&#xff08;有1004页&#xff09;&#xff0c;读起来颇费功夫&#xff0c;但作为JavaScript&a…

华为交换机如何通过tftp服务器上传下载文件

温馨提示:为了防止更新交换机配置文件操作失败,操作前交换机配置文件一定要做备份,可以通过copy vrpcfg.zip vrpcfg_backup.zip 工具/原料: windows电脑: Tftpd64 SecureCRT USB转串口,串口转RJ45 console线 2米网线 华为交换机S3700一台 方法/步骤: 1.准备材料,按照下…

Maven排除项目中同名不同版本的jar

今天突然发现web项目打包后的exe居然有200M了&#xff0c;心想不应该有这么大的啊&#xff0c;于是检查了一番发现引用的jar有130个&#xff0c;仔细一瞅发现好多同名的但是不同版本的jar&#xff0c;比如说有commons-httpclient就有两个&#xff0c;3.0和3.1版本的。这样直接导…

js节流函数和js防止重复提交的N种方法

应用情景 经典使用情景&#xff1a;js的一些事件&#xff0c;比如&#xff1a;onresize、scroll、mousemove、mousehover等&#xff1b; 还比如&#xff1a;手抖、手误、服务器没有响应之前的重复点击&#xff1b; 这些都是没有意义的&#xff0c;重复的无效的操作&#xff…

使用IPFS集群搭建创建私有IPFS网络

基本介绍 IPFS 网络分两类: 公有私有 对于大多数商用应用尤其是企业级解决方案而言&#xff0c;需要对自身数据有完全的控制&#xff0c;这种场合公有IPFS网络并不适用&#xff0c;搭建私有IPFS往往是这类应用的必要需求。 本文我们讲解创建一个私有 IPFS 网络的过程&#xf…

华为交换机S3700-TELNET远程管理交换机配置

组网需求: 1.PC 通过telnet 登陆交换机并对其进行管理(本演示以Ethernet0/0/1作为管理端口); 2.仅密码认证方式; 3.只允许192.168.2.1的IP地址的PC TELNET 访问。 首先开启telnet服务 <huawei>system-view Enter system view, return user view with Ctrl+Z. […

MongoDB基础介绍安装与使用

MongoDB已经日益成为流程和主流的数据库了&#xff0c;原因有两个&#xff1a;第一个就是技术优势&#xff0c;第二就是便利性&#xff0c;个人使用部署都很方便。 MongoDB的优缺点&#xff0c;以及使用场景 优点&#xff1a; 面向文档存储&#xff08;自由读高&#xff0c;…

转载:MySQL数据库INSERT、UPDATE、DELETE以及REPLACE语句的用法详解

转自&#xff1a;http://www.jb51.net/article/39199.htm 本篇文章是对MySQL数据库INSERT、UPDATE、DELETE以及REPLACE语句的用法进行了详细的分析介绍&#xff0c;需要的朋友参考下MySQL数据库insert和update语句引:用于操作数据库的SQL一般分为两种&#xff0c;一种是查询语句…

python调用cv2.findContours时报错:ValueError: not enough values to unpack (expected 3, got 2)

python调用cv2.findContours时报错&#xff1a;ValueError: not enough values to unpack (expected 3, got 2) OpenCV旧版&#xff0c;返回三个参数&#xff1a; im2, contours, hierarchy cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) 要想返回三个参数…

华为S5700交换机开启telnet远程指定IP登陆

1,交换机开启Telnet服务 <Huawei>system-view Enter system view, return user view with Ctrl+Z. [Huawei]sysname S2[S2]telnet server enable //使能telnet服务。2.配置登陆IP地址 [S2]interface Vlanif 1 //配置IP地址 [S2

一个好的技术团队应该怎么选择开发语言

在过去的三年时间了&#xff0c;作为曾经的研发部经理&#xff0c;我和我的技术总监始终在为一件事而努力着&#xff0c;那就是选择一门合适我们团队的技术语言。 我们研发团队一共有9个人&#xff0c;分为三个小组&#xff1a;移动手机组、后端接口组、web前端组&#xff0c;…

优先级队列用的的数据结构

2019独角兽企业重金招聘Python工程师标准>>> 优先级队列和队列没有本质的区别 只是 每次出队列的时候出队列中优先级最高的 这里假定数字越小&#xff0c;优先级越高 优先级队列 是不同于先进先出队列的另一种队列。每次从队列中取出的是具有最高优先权的元素。 通…

L-BFGS算法/Broyden族/BFGS算法/阻尼牛顿法的Python实现代码

下面定义了三个Python语言编写的函数&#xff1a;函数表达式fun&#xff0c;梯度向量gfun&#xff0c;和海森矩阵hess。这三个表达式在后面各个算法的实现中会用到。 # 函数表达式fun fun lambda x:100*(x[0]**2-x[1])**2 (x[0]-1)**2# 梯度向量 gfun gfun lambda x:np.arr…

公网访问阿里云数据库MongoDB——填坑笔记

业务情景 两台服务器&#xff0c;一台阿里云ECS云服务器&#xff08;专用网络&#xff09;&#xff0c;另一台是阿里云数据库MongoDB&#xff0c;处于安全考虑MongoDB是不运行外网连接的&#xff0c;那接下来就看怎么实现公网访问。 看到上面红色的网络类型描述&#xff0c;有…

华为S5700交换机开启telnet远程登陆配置(推荐)

实验目标: 电脑PC1经过S3700交换机,telnet远程登录到S5700交换机。 连接拓扑图如下:(测试时用实物测试) 一、配置S5700交换机。 1.交换机开启Telnet服务 <Huawei>system-view Enter system view, return user view with Ctrl+Z. [Huawei]sysname LSW1 [LSW1]