Mvc下异步断点续传大文件
最近公司一同事咨询了一个MVC项目下上传大文件时遇到的问题,问题描述如下:

MVC项目中,当上传比较大的文件时,速度非常慢,小文件基本没有影响。
原因分析:
如果是用传统的form表单去提交的话,会将整个文件一次性的加载到内存中然后再做保存,这个过程是相当慢的,特别是大文件,且上传文件容易受到各种因素的影响而导致上传失败,比如临时的网络故障等。
如何解决?

最直接的概念就是异步以及断点续传。
为什么要异步
- 如果一个表单提交的元素中有文件上传的需求,如最终因为文件上传失败而影响整个表单数据的提交,这个体验性是非常差的。
- 如果上传文件时间特别长,容易使应用程序长时间失去响应,给用户一个错觉,最好的方法是先让用户选择文件,此时点击上传,后台进行文件的异步上传,此时用户还可以继续去填写其它的表单元素,等用户填写完其它表单元素,文件有可能已经上传完成了,再提交表单,就只处理数据而不再上传文件了。增强了用户体验性。
为什么要断点续传
在处理大文件时,无法忍受因为一时的网络原因导致上传失败,从而重新再上传的烦恼。好的方法是将一个大文件分成N个小块来进行上传,即使第一次失败了,之前上传的那部分由于得到了保留,再次点击上传时,以前已经传输成功的部分就不会再次被重新写入文件。注意,第二次上传时,文件还是从0开始传输到服务器,而不能根据服务器上的文件选择性的传输片断,这部分不太好节省,有兴趣的可以研究下。
如何实现异步上传
这里可以利用jQuery的相应插件来完成,它的主要功能是将文件分割成N多个小块来批量上传,参考网址:https://github.com/blueimp/jQuery-File-Upload
如何实现断点续传
其实这个也非常简单,在Http头信息中有一个Conten-Range的属性,它会说明此次传递的文件内容的片断范围,我们只需要在后台解析这个范围稍加处理就可以实现。之所这么简单,是因为有了上面的jQuery 上传文件的插件,它负责将一个大文件分成N多小块进行传输,这就有了请求头中的Content-Range。
我主要参考了http://weblogs.asp.net/bryansampica/archive/2013/01/15/AsyncMVCFileUpload.aspx ,但它没有完成对文件的保存功能,我这里加了断点续传的逻辑。
组件对于浏览器的要求
看到大家对于js分割文件上传产生了不解,其实这个jQuery组件对浏览器是有要求的,利用了一些全新的api,上面文章中有提到浏览器的要求,对于只能使用低版本浏览器的项目来讲可能目前还有些问题,但异步上传是没问题的。
Here it is running in Chrome: (Note: In Chrome, Firefox, and Safari you'll get a multiple file selection, in IE 9 and under, you wont. IE 10 you will)
效果图:

可支持同时上传多个文件

上传成功后的效果

实现关键步骤:

- 引入jQuery上传文件组件
- 初始化上传组件
主要是控制进展条的显示以及组件的部分参数,比如文件块大小等等。
$(function () {
$('#fileupload').fileupload({
dataType: "json",
url: "/api/upload",
limitConcurrentUploads: 1,
sequentialUploads: true,
progressInterval: 100,
maxChunkSize: 10000,
add: function (e, data) {
$('#filelistholder').removeClass('hide');
data.context = $('<div />').text(data.files[0].name).appendTo('#filelistholder');
$('</div><div class="progress"><div class="bar" style="width:0%"></div></div>').appendTo(data.context);
$('#btnUploadAll').click(function () {
data.submit();
});
},
done: function (e, data) {
data.context.text(data.files[0].name + '... Completed');
$('</div><div class="progress"><div class="bar" style="width:100%"></div></div>').appendTo(data.context);
},
progressall: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
$('#overallbar').css('width', progress + '%');
},
progress: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
data.context.find('.bar').css('width', progress + '%');
}
});
});
3. 完成断点续传后台逻辑
- 首先通过api来配合jQuery上传组件对于上传进展条的一个监控 。
[HttpGet]
[HttpPost]
public HttpResponseMessage Upload()
{
// Get a reference to the file that our jQuery sent. Even with multiple files, they will all be their own request and be the 0 index
HttpPostedFile file = HttpContext.Current.Request.Files[0]; // do something with the file in this space
// {....}
// end of file doing
this.SaveAs(HttpContext.Current.Server.MapPath("~/Images/") + file.FileName, file); // Now we need to wire up a response so that the calling script understands what happened
HttpContext.Current.Response.ContentType = "text/plain";
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var result = new { name = file.FileName }; HttpContext.Current.Response.Write(serializer.Serialize(result));
HttpContext.Current.Response.StatusCode = 200; // For compatibility with IE's "done" event we need to return a result as well as setting the context.response
return new HttpResponseMessage(HttpStatusCode.OK);
}
- 实现断点续传逻辑
这其中主要是通过解析Http请求头中的Content-Range属性来获知此次处理的文件片断,后续就是基本的文件操作了,没什么可说的。
private void SaveAs(string saveFilePath, HttpPostedFile file)
{
long lStartPos = 0;
int startPosition = 0;
int endPosition = 0;
var contentRange=HttpContext.Current.Request.Headers["Content-Range"];
//bytes 10000-19999/1157632
if (!string.IsNullOrEmpty(contentRange))
{
contentRange = contentRange.Replace("bytes", "").Trim();
contentRange = contentRange.Substring(0, contentRange.IndexOf("/"));
string[] ranges = contentRange.Split('-');
startPosition = int.Parse(ranges[0]);
endPosition = int.Parse(ranges[1]);
}
System.IO.FileStream fs;
if (System.IO.File.Exists(saveFilePath))
{
fs = System.IO.File.OpenWrite(saveFilePath);
lStartPos = fs.Length; }
else
{
fs = new System.IO.FileStream(saveFilePath, System.IO.FileMode.Create);
lStartPos = 0;
}
if (lStartPos > endPosition)
{
fs.Close();
return;
}
else if (lStartPos < startPosition)
{
lStartPos = startPosition;
}
else if (lStartPos > startPosition && lStartPos < endPosition)
{
lStartPos = startPosition;
}
fs.Seek(lStartPos, System.IO.SeekOrigin.Current);
byte[] nbytes = new byte[512];
int nReadSize = 0;
nReadSize = file.InputStream.Read(nbytes, 0, 512);
while (nReadSize > 0)
{
fs.Write(nbytes, 0, nReadSize);
nReadSize = file.InputStream.Read(nbytes, 0, 512);
}
fs.Close();
}
总结:
jQuery上传组件将大文件分割成小文件上传,正好解决了.net上传文件大小问题,只要将块大小配置好即可。利用Http头信息的Content-Range来实现断点续传,即解决了性能问题也解决了用户体验。
注:
这只是我个人测试的代码,如觉的不妥,可自行修改。
Mvc下异步断点续传大文件的更多相关文章
- CentOS下如何查找大文件
在Windows系统中,我们可以使用TreeSize工具查找一些大文件或文件夹,非常的方便高效,在Linux系统中,如何去搜索一些比较大的文件呢?下面我整理了一下在Linux系统中如何查找大文件或文件 ...
- CephRGW 在多个RGW负载均衡场景下,RGW 大文件并发分片上传功能验证
http://docs.ceph.com/docs/master/radosgw/s3/objectops/#initiate-multi-part-upload 根据分片上传的API描述,因为对同一 ...
- asp.NET 下真正实现大文件上传
一般10M以下的文件上传通过设置Web.Config,再用VS自带的FileUpload控件就可以了,但是如果要上传100M甚至1G的文件就不能这样上传了.我这里分享一下我自己开发的一套大文件上传控件 ...
- 30分钟玩转Net MVC 基于WebUploader的大文件分片上传、断网续传、秒传(文末附带demo下载)
现在的项目开发基本上都用到了上传文件功能,或图片,或文档,或视频.我们常用的常规上传已经能够满足当前要求了, 然而有时会出现如下问题: 文件过大(比如1G以上),超出服务端的请求大小限制: 请求时间过 ...
- 在.net MVC中异步上传图片或者文件
@using (Ajax.BeginForm("AddMessages", "MenuInfo", new AjaxOptions { HttpMethod ...
- IIS7下swfupload上传大文件出现404错误
要求上传附件大小限制在2G,原本以为可以轻松搞定.在编译模式下可以上传大文件,可是在IIS7下(自己架的服务器),一上传大的文件就会出现 Http 404错误,偶尔有的文件还有IO. error错误. ...
- Linux下查找大文件以及目录
转自:http://www.cnblogs.com/kerrycode/p/4391859.html 在Windows系统中,我们可以使用TreeSize工具查找一些大文件或文件夹,非常的方便高效,在 ...
- Linux如何查找大文件或目录总结
在Windows系统中,我们可以使用TreeSize工具查找一些大文件或文件夹,非常的方便高效,在Linux系统中,如何去搜索一些比较大的文件呢?下面我整理了一下在Linux系统中如何查找大文件或文件 ...
- Linux如何查找大文件或目录总结及在全部目录中查找
在Windows系统中,我们可以使用TreeSize工具查找一些大文件或文件夹,非常的方便高效,在Linux系统中,如何去搜索一些比较大的文件呢?下面我整理了一下在Linux系统中如何查找大文件或文件 ...
随机推荐
- 纯CSS3实现3D特效的iPhone 6动画
iPhone 6发布不久,屌丝怎能买得起,不过作为程序员,今天看到一个用纯CSS3绘制的iPhone 6,由于CSS3特性的运用,带有点3D的动画特效,大家可以先来看看在线演示效果. 在线演示 ...
- 配置ini指定eclipse启动JDK版
eclipse mars1 需要JDK 1.7+ 解决方案: 改eclipse.ini配置文件 -startupplugins/org.eclipse.equinox.launcher_1.3.100 ...
- C代码中如何调用C++ C++中如何调用C
注意这里的C调用C++或者C++调用C意思是.c文件中调用.cpp文件中代码,或者相反. 集成开发环境如VC++6.0或者vs都是以文件后缀来区别当前要编译的是C代码还是C++代码,然后采用响应的编译 ...
- Spring Boot 之 RESRful API 权限控制
摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢! “简单,踏实~ 读书写字放屁” 一.为何用RESTful API 1.1 RESTful是什么? ...
- WindowsStore页面导航
第一个页面:前台 <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> &l ...
- 关于IT概念的一些思考
同事提及“软件工程.软件生命周期.项目管理.CMMI.IPD.RUP.UML及UML建模.面向对象分析与设计.需求分析.系统分析与设计……等等,它们到底是什么?它们之间有什么关系?” 下面是个人见 ...
- 百度地图api根据定位获取附近商家(只获取屏幕内)
根据中心点坐标计算出屏幕2个点(一个最低经纬度,一个最高经纬度),判断这两个点中间的所有坐标的商家..考虑屏幕分辨率之类 移动地图中心点变动,如何异步刷新,判断商家是否已经存在..等... 百度地图a ...
- 【软件分析与挖掘】Multiple kernel ensemble learning for software defect prediction
摘要: 利用软件中的历史缺陷数据来建立分类器,进行软件缺陷的检测. 多核学习(Multiple kernel learning):把历史缺陷数据映射到高维特征空间,使得数据能够更好地表达: 集成学习( ...
- SNF开发平台WinForm之八-自动升级程序部署使用说明-SNF快速开发平台3.3-Spring.Net.Framework
9.1运行效果: 9.2开发实现: 1.首先配置服务器端,把“SNFAutoUpdate2.0\服务器端部署“目录按网站程序进行发布到IIS服务器上. 2.粘贴语句,生成程序 需要调用的应用程序的Lo ...
- 测试GeoGebra博客
已知函数 \(\textit{f}(\textit{x})=2\textit{m}\ln\textit{x}-\textit{x}^2\), \(\textit{g}(\textit{x})=\tex ...