博客园::首页::  ::  ::  ::管理
5 Posts :: 0 Stories :: 75 Comments :: 0 Trackbacks

MVC&WebForm对照学习:文件上传(以图片为例)

在web应用中,文件上传是个很普遍的功能,那么今天就来小结一下asp.net中文件上传的方式。首先我们快速来回忆一下WebForm中的文件上传的方法。

Part 1 WebForm中的文件上传

FileUpload服务器控件

aspx:

<div>
<asp:Image ImageUrl="~/uploads/1.jpg" ID="img2" runat="server" Width="150px" Height="150px" />
<asp:FileUpload runat="server" ID="fupImage" />
<input type="button" value="上传" id="btnSubmit" runat="server" onserverclick="btnSubmit_ServerClick" />
</div>

aspx.cs:

protected void btnSubmit_ServerClick(object sender, EventArgs e)
{
if (fupImage.HasFile)
{
Regex regex = new Regex(@".(?i:jpg|jpeg|gif|png)$");
if (regex.IsMatch(Path.GetExtension(fupImage.FileName)))
{
string path = AppDomain.CurrentDomain.BaseDirectory + "uploads";
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
string filePath = fupImage.FileName; //此处需要处理同名文件
fupImage.SaveAs(Path.Combine(path, filePath));
img2.ImageUrl = "~/uploads/" + filePath;
}
}
}

运行结果:

Note:如果image是普通的html服务器控件,那么后台赋值就要这样:

img1.Src = "~/uploads/" + filePath;

Html服务器控件

aspx:

<div>
<asp:Image ImageUrl="~/uploads/1.jpg" ID="img2" runat="server" Width="150px" Height="150px" />
<input type="file" runat="server" id="fileimg" />
<input type="button" value="上传" id="btnSubmit" runat="server" onserverclick="btnSubmit_ServerClick" />
</div>

aspx.cs:

if (fileimg.PostedFile.ContentLength > 0)
{
string fileName = Path.GetFileName(fileimg.PostedFile.FileName);
Regex regex = new Regex(@".(?i:jpg|jpeg|gif|png)$");
if (regex.IsMatch(Path.GetExtension(fileName)))
{
string path = AppDomain.CurrentDomain.BaseDirectory + "uploads";
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
fileimg.PostedFile.SaveAs(Path.Combine(path, fileName));
img2.ImageUrl = "~/uploads/" + fileName;
}
}

运行也能实现同样的效果。

普通的Html标签

aspx:

<div>
<asp:Image ImageUrl="~/uploads/1.jpg" ID="img2" runat="server" Width="150px" Height="150px" />
<input type="file" runat="server" id="fileimage"/>
<input type="button" value="上传" id="btnSubmit" runat="server" onserverclick="btnSubmit_ServerClick" />
</div>

aspx.cs

if (Request.Files["fileimage"].HasFile())
{
string fileName = Path.GetFileName(Request.Files["fileimage"].FileName); //此处位需要处理同名文件
Regex regex = new Regex(@".(?i:jpg|jpeg|gif|png)$");
if (regex.IsMatch(Path.GetExtension(fileName)))
{
string path = AppDomain.CurrentDomain.BaseDirectory + "uploads";
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
Request.Files["fileimage"].SaveAs(Path.Combine(path, fileName));
img2.ImageUrl = "~/uploads/" + fileName;
}
}

Note:以上仅仅为了说明问题,故省去了对文件size的判断。

回过头来在看看,我们会发现如下关系:

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

再来看看它们最终save的方法:

---------------------------html服务器控件------------------------------------------

---------------------------html标签------------------------------------------------

Note:由于服务器控件FileUpload,我暂时还没办法通过反序列化查看到内部细节,故缺少图片。

通过上面三种不同的标签的实现方式,可以看出基本上都是殊途同归。因为我们知道服务器控件只是封装了某些东西,(虽然通过反编译有些代码还是查看不到)我们完全可以揣测,这种实现其实就是最终第三种方式来实现的,使我们操作起来更加方便而已,它最终还是要转换成普通的html标签。因此服务器控件的性能相比较而言有所损失。但是由于它会根据不同的浏览器生成一些样式和脚本,因此兼容性会较好。那么抛开兼容性不说,既然它们最终的实现方式是一样的(HttpPostedFile对象),那么我们完全可以抽象出一个共通的方法来实现,以省去每次使用它们的时候要写不同的处理方式。一下以html控件为例:

(注1)

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

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

public static void Upload(string filePath)
{
var request = System.Web.HttpContext.Current.Request;
foreach (string upload in request.Files)
{
HttpPostedFile hp = request.Files[upload];
if (hp.HasFile())
{
CreateFolderIfNeeded(filePath);
string fileName = Path.GetFileName(hp.FileName); //此处位需要处理同名文件
hp.SaveAs(Path.Combine(filePath, fileName));
}
}
}

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

private static void CreateFolderIfNeeded(string path) {
if (!Directory.Exists(path)) {
try {
Directory.CreateDirectory(path);
}
catch (Exception) {
/*TODO: You must process this exception.*/
//throw new ArgumentException(Environment.GetResourceString("Argument_PathEmpty"));
}
}
}

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

public static class HttpPostFileExtensions {   //扩展方法必须在顶级类中定义
public static bool HasFile(this HttpPostedFile file) {
return (file != null && file.ContentLength > 0) ? true : false;
}
}

注1:特别注意的是由于Request.Files是名称值对的集合,而名称正是html标签的name属性的值,故使用普通的html控件的时候需要给file标签加上name属性,否则后台无法获取到它的值。

Part 2 MVC中的文件上传

如果习惯了使用WebForm服务器控件开发,那么初次接触MVC(本文以razor为例),你会发现这些服务器控件已经派不上用场了,就以文件上传为例,我们没办法像以前那样使用FileUpload愉快地拖曳来实现文件上传了。当然了所有的ASP.NET服务器控件也好,html服务器控件也好包括MVC的Htmlhelper,这些最终都要生成普通的html标签。而且MVC和WebForm都是基于ASP.NET平台的,那么从这2个点来说,我们在上文中最后提供的一个抽象封装(当然这只是一个简单的demo,它不能满足所有的实际开发中的变化了的需求)方法按道理来说也适用于MVC,那么究竟是不是这样呢?小段代码为证:

@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { id = "form1", enctype = "multipart/form-data" }))
{
<img src="/uploads/1.jpg" alt="暂无图片" id="img1" style="width:150px;height:150px;" />
<input type='file' name='file' id='file' />
<input type="button" value="上传" id="btnSubnit" />
}
<script src="~/Scripts/jquery-1.4.1.min.js"></script>
<script src="~/Scripts/jquery-form.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$("#file").bind("change", function () {
$("#form1").ajaxSubmit({
success: function (data) {
$("#img1").attr("src", data.filePath);
}
});
});
});
</script>

---------------------------------------------------------------------请允许我丑陋的展现方式-------------------------------------------------------------------------------------

[HttpPost]
public ActionResult Index(HttpPostedFileBase file/*这里的参数暂时无用*/)
{
string path = AppDomain.CurrentDomain.BaseDirectory + "uploads";
UpLoadFileHelper.Upload(path);
var filePath = "/uploads/" + Path.GetFileName(Request.Files["file"].FileName);
return Json(new { filePath = filePath });
}

---------------------------------------------------------------------运行结果---------------------------------------------------------------------------------------------------

Note:以上使用了UpLoadFileHelper.Upload只是为了说明问题。实际开发中随着页面需求的变化,这个实现也要进行重构,也恳请博友能够提供完美的方案。

在WebForm中有HttpPostedFile对象,同样的在MVC中也有HttpPostedFileBase(我不知道微软是不是有意在名字上加以区分)。其实它们反应到上下文中是一样的。像这样的情况还有很多,比方说细心的你一定会发现,在Controller中取到的HttpContext是HttpContextBase而在WebForm中是HttpContext。虽然他们是两个不同的对象,但是内部实现是一样的(当然我没有查阅所有的这些类似的对象,也不保证所有的这些类似对象都是同样的实现)。那么我们就使用HttpPostedFileBase来看看MVC中如何实现单个文件的上传的:

[HttpPost]
public ActionResult Index(HttpPostedFileBase file)
{
if (null != file && file.ContentLength>0)
{
string path = AppDomain.CurrentDomain.BaseDirectory + "uploads";
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
string fileName = string.Empty;
fileName = Path.GetFileName(file.FileName);
file.SaveAs(Path.Combine(path, fileName));
return Json(new { filePath = "/uploads/" + fileName, code = 1 });
}
return Json(new { filePath = "请选择需要上传的文件", code = 0 });
}

同样也能实现同样的效果。

Note:由于HttpPostedFileBase是从Request.Files["name"]得到的,因此Action中HttpPostedFileBase的变量名必须保持和View中file标签的name属性值一致。

Part 3 问题开发

    关于input type="file"的style

众所周知,file input标签在不同的浏览器中会展现不同的样式,在实际开发中,针对这个也有很多很好的解决方案,这个可以baidu或者google(说道此,满眼是泪,希望博友可以 提供海量无需FQ即可访问google的网址 ,在此不胜感激!!!)。我在这里引用 WebMagazine 网站一个解决方案,只要代码如下:

---------------------------------------------------------------------HTML标签---------------------------------------------------------------------------------------------------

<div class="file-wrapper">
<input type="file" />
<span class="button">Choose a file</span>
</div>

---------------------------------------------------------------------jQuery-----------------------------------------------------------------------------------------------------

<script src="jquery.js"></script>
<script type="text/javascript">
var SITE = SITE || {}; SITE.fileInputs = function () {
var $this = $(this),
$val = $this.val(),
valArray = $val.split('\\'),
newVal = valArray[valArray.length - 1],
$button = $this.siblings('.button'),
$fakeFile = $this.siblings('.file-holder');
if (newVal !== '') {
$button.text('File Chosen');
if ($fakeFile.length === 0) {
$button.after('<span class="file-holder">' + newVal + '</span>');
} else {
$fakeFile.text(newVal);
}
}
}; $(document).ready(function () {
$('.file-wrapper input[type=file]').bind('change focus click', SITE.fileInputs);
});
</script>

---------------------------------------------------------------------Css---------------------------------------------------------------------------------------------------------

<style type="text/css">
.file-wrapper {
position: relative;
display: inline-block;
overflow: hidden;
cursor: pointer;
} .file-wrapper input {
position: absolute;
top: 0;
right: 0;
filter: alpha(opacity=1);
opacity: 0.01;
-moz-opacity: 0.01;
cursor: pointer;
} .file-wrapper .button {
color: #fff;
background: #117300;
padding: 4px 18px;
margin-right: 5px;
border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
display: inline-block;
font-weight: bold;
cursor: pointer;
} .file-holder {
color: #000;
}
</style>

完整代码下载

    关于ASP.NET中input type="file"单次请求上传文件超过默认(ASP.NET为4M,IIS7为30M)时处理异常

在ASP.NET Web开发中,这也不是什么新问题,在这里我想既然说到了文件上传,那么不得不在老调重弹一下,而且网上虽说对于这个问题早有定论,但是还是会看到不少对ASP.NET和IIS默认限制大小的具体说法有些混乱。测试环境为iis7+asp.net 4.0。

  Situation 1:不改默认配置的情况下使用FileUpload上传一个4.69M的文件:

    Situation 2:不改默认配置的情况下使用FileUpload上传一个40M的文件:

另外更糟糕的是甚至会出现这种情况:

从不同的错误页面不难看出,asp.net 默认限制上传大小不超过4M,运行时间不超过90s(因此会出现第三种错误的页面),iis7环境下为30M,超过默认设置,IIS会认为用户是在恶意攻击,因此会拒绝请求。当然微软考虑到上传到文件的需求,因此这个问题是可以通过配置解决的。那么针对它们的解决办法:

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

Note1:关于此问题的更详细的解决办法,可以参考 Large File Upload in IIS

Note2:不仅如此ASP.NET和IIS对其它的诸如QueryString、Url也都有限制。比方说,当文本框输入的字节超过2048个字节的时候同样会引发异常。我想之所以网上关于这个问题的解读会出现偏差可能是这于这两者没有仔细查看的缘故吧。我想如果是这样的话,那么我们就需要仔细看看这两个配置借点的相信阐述了: httpRuntimerequestFiltering

Part 4 拓展

另外说到这么多file的问题,其实我们常常看到有些网站会有上传进度条、图片剪切、拖曳上传、异步上传等,而如果要在file基础上实现这个,还是有点麻烦的。我们使用第三方组件来实现。这个在百度上也能找到能多既有的方案。我推荐一个能够实现多文件和进度条上传的组件 jQuery Multiple File Upload with Progress bar Tutorial 。文件上传的 jQuery File Upload Demo 以及 jQuery File Upload 。唉,好的插件确实是太多了,看得我眼花缭乱,不禁要感叹,再也不用担心文件上传了。根据自己的需要选择吧。

Part 5 参阅链接

http://www.codeproject.com/Articles/442515/Uploading-and-returning-files-in-ASP-NET-MVC

http://weblogs.asp.net/bryansampica/AsyncMVCFileUpload

http://ajaxuploader.com/large-file-upload-iis-asp-net.htm

http://ajaxuploader.com/large-file-upload-iis-asp-net.htm

http://msdn.microsoft.com/zh-cn/library/ms689462.aspx

Part 6 The end

注:由于个人技术有限,对某些概念的理解可能会存在偏差,如果你发现本文存在什么bug,请指正。谢谢!

MVC&WebForm对照学习:文件上传(以图片为例)的更多相关文章

  1. MVC&WebForm对照学习:文件下载

    说完了WebForm和MVC中的文件上传,就不得不说用户从服务器端下载资源了.那么今天就扯扯在WebForm和MVC中是如何实现文件下载的.说起WebForm中的文件上传,codeshark在他的博文 ...

  2. THINKPHP源码学习--------文件上传类

    TP图片上传类的理解 在做自己项目上传图片的时候一直都有用到TP的上传图片类,所以要进入源码探索一下. 文件目录:./THinkPHP/Library/Think/Upload.class.php n ...

  3. Spring4 MVC 多文件上传(图片并展示)

    开始需要在pom.xml加入几个jar,分别是 <dependency> <groupId>commons-fileupload</groupId> <art ...

  4. Spring MVC(四)文件上传

    文件上传步骤 1.写一个文件上传的页面 2.写一个文件上传的控制器 注意: 1.method="post" 2.enctype="multipart/form-data& ...

  5. spring mvc 3.0 实现文件上传功能

    http://club.jledu.gov.cn/?uid-5282-action-viewspace-itemid-188672 —————————————————————————————————— ...

  6. Spring MVC—拦截器,文件上传,中文乱码处理,Rest风格,异常处理机制

    拦截器 文件上传 -中文乱码解决 rest风格 异常处理机制 拦截器 Spring MVC可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义的拦截器必须实现HandlerI ...

  7. webform文件上传、图片水印、验证码

    文件上传: 所用控件:FileUpload 使用时的思路: 1.判断用户是否选中了文件 FileUpload.FileName获取选中的文件名,判断长度,如果长度大于零就代表已经选择了文件 JS端:通 ...

  8. SpringMVC学习--文件上传

    简介 文件上传是web开发中常见的需求之一,springMVC将文件上传进行了集成,可以方便快捷的进行开发. springmvc中对多部件类型解析 在 页面form中提交enctype="m ...

  9. [六]SpringMvc学习-文件上传

    1.单文件上传 1.1修改配置文件 <bean id="multipartResolver" class="org.springframework.web.mult ...

随机推荐

  1. Activity界面切换动画特效。

    效果图: 结构图: 测试代码: 布局: 1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearL ...

  2. 浅谈C++源码的过国内杀软的免杀

    以下只是简单的思路和定位.也许有人秒过,但是不要笑话我写的笨方法.定位永远是过期不了的. 其实这里废话一下 , 本人并不是大牛 ,今天跟大家分享下 .所以写出这篇文章.(大牛飘过) 只是个人实战的经验 ...

  3. HDU 4405 Aeroplane chess 概率DP 难度:0

    http://acm.hdu.edu.cn/showproblem.php?pid=4405 明显,有飞机的时候不需要考虑骰子,一定是乘飞机更优 设E[i]为分数为i时还需要走的步数期望,j为某个可能 ...

  4. 【第40套模拟题】【noip2011_mayan】解题报告【map】【数论】【dfs】

    目录:1.潜伏者 [map] 2.Hankson的趣味题[数论]3.mayan游戏[dfs] 题目: 1. 潜伏者(spy.pas/c/cpp)[问题描述]R 国和S 国正陷入战火之中,双方都互派间谍 ...

  5. IE11无法 登陆银行网站

    1,打开IE11,看着键盘,按住Alt+X,然后按字母O打开IE设置选项=>[安全]选项卡把安全级别拉到最下,关闭[启用保护模式] 2,点击[受信任的站点]将支付宝和农业银行网址添加进去,关闭选 ...

  6. K2 BPM+Microsoft Dynamics CRM,妥妥的~

    啊~~~~七夕 ▼ 你比巴西少一xi 你比山西多四xi 对有情人来说今天就是情人节,对单身汪来说,今天就是个星期四. but,软件也是要秀恩爱的! ♥ 晒晒我家亲爱的CRM,它的全名叫Microsof ...

  7. VS设置背景色减缓眼睛疲劳

    工具--选项--字体和颜色--(纯文本)项背景色--自定义... 色调:85 饱和度:123 亮度:205 可自己微调 字体设为10.

  8. 介绍NSURLSession网络请求套件

    昨天翻译了一篇<NSURLSession的使用>的文章,地址:http://www.cnblogs.com/JackieHoo/p/4995733.html,原文是来自苹果官方介绍NSUR ...

  9. D.T SOFTWARE (1) 软件架构直播答疑课程

    今晚的d.t课程 1项目需求 PPTP服务搭建完成PPTP服务器的搭建,用户重新拨号获得新IP后,要求拔PPTP VPN成功时,也获取到新的公网IP,而且能通过代理上网.VNC服务安装用户可以通过VN ...

  10. Ogre 1.8 terrain 和 paging 组件

    以下转自:http://hi.baidu.com/xocoder/item/e8d87cf53d87612b753c4cfd OGRE地形生成 OGRE可以通过两个接口来生成地形,分别是void Te ...