asp.net单文件带进度条上传的解决方案
最近做项目中遇到很多问题,比如带进度条的文件上传,看了网上很多资料还没找到真正意义上的asp.net实现进度条上传(可能是我没找到),下面我来跟大家分享一下我实现的这个程序。
首先看下界面效果,当然你可以完全修改界面为你自己所用。
先解释一下这个程序,该程序采用了jquery框架,实现了小文件上传,不超过80mb,可以在web.config文件中进行相应的配置,但是有个最大值,具体需要查看msdn。开发环境采用visual studio 2013 .net framework 4.5,运行的时候大家注意一下是否满足要求,好了,下面直入正题。
先来看看实现原理。基本原理:一个页面进行文件上传,另外一个页面去监听这个文件上传了多少。
这里面有两个地方需要解释一下:第一个,如何知道监听的这个文件就是上传的这个文件?实现机制很简单,就是让asp.net产生一个唯一的guid,这个id序号是唯一的,通过ajax取出来赋值给一个隐藏字段;第二个,如何获取guid标志的文件信息?通过asp.net缓存机制实现,上传的过程中,不断的将上传信息往缓存里面写,直到文件上传完成,而在另外一个通过guid获取缓存的信息,信息包括你想要的信息,比如上传了多少字节、消耗了多长时间等。好了,要点就解释到这里,有疑问的话给我留言。
下面来说说具体的实现:
文件目录结构如下:
index.htm就是文件上传页面,提交form给uploadhandler目录下的default.aspx,以实现文件上传。
progresshandler目录下三个文件为abort.ashx、genericguid.ashx,handler.ashx功能分别为:根据guid取消正在上传的文件,生成guid,根据guid获取上传信息。
第一步:建立index.htm页面,这个上传页面,需要注意的就是需要一个隐藏的iframe,并且名字为form提交的目标。
<!doctype html public "-//w3c//dtd xhtml 1.0 transitional//en" "http://www.w3.org/tr/xhtml1/dtd/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>asp.net ajax文件上传进度条示例</title> <meta name="author" content="李检全" /> <link href="styles/base.css" rel="stylesheet" type="text/css" /> <script src="scripts/jquery-1.4.2.min.js" type="text/javascript"></script> <script src="scripts/jquery-ui-1.8.2.custom.min.js" type="text/javascript"></script> <script src="scripts/ljq.lib.js" type="text/javascript"></script> <script src="scripts/ajax/guidget.js" type="text/javascript"></script> <script src="scripts/ajax/ajax-progress-upload.js" type="text/javascript"></script> </head> <body> <div id="upload_demo"> <div class="title">asp.net ajax 文件上传进度条示例</div> <form action="uploadhandler/default.aspx" enctype="multipart/form-data" method="post" target="upload_hidden_iframe"> <input id="guid" name="guid" value="" type="hidden" /> <p>*本程序适合小文件上传,不超过80mb</p> <p>文件地址</p> <input name="upload_file" type="file" /> <br /> <p>文件描述</p> <textarea name="description_file"></textarea> <br /> <br /> <input type="submit" value="上传文件" /> </form> </div> <div id="back_panel"></div> <div id="upload_panel"> <div id="upload_title">文件上传</div> <div id="upload_content"> <ul> <li id="finished_percent">正在准备上传...</li> <li><div id="upload_bar"><div id="upload_progress"></div></div></li> <li id="upload_speed"></li> <li id="upload_costtime"></li> <li id="upload_filesize"></li> <li id="upload_filename"></li> </ul> <div id="upload_detail"></div> <div id="upload_choose"> <span id="upload_cancel">取消</span><span id="upload_submit">确定</span> </div> </div> </div> <iframe name="upload_hidden_iframe" style="display:none;"></iframe> </body> </html>
第二步,创建generateguid.ashx文件,作用就是生成唯一的guid。
<%@ webhandler language="c#" class="progresshandler.handler" %> using system; using system.web; using system.xml.linq; namespace progresshandler { public class handler : ihttphandler { /// <summary> /// 获得上传文件的guid /// </summary> /// <param name="context">当前请求实体</param> /// <creattime>2015-06-28</creattime> /// <author>freshman</author> public void processrequest(httpcontext context) { context.response.charset = "utf-8"; context.response.contenttype = "application/xml"; var guid = guid.newguid().tostring(); var doc = new xdocument(); var root = new xelement("root"); var xguid = new xelement("guid", guid); root.add(xguid); doc.add(root); context.response.write(doc.tostring()); context.response.end(); } public bool isreusable { get { return false; } } } }
第三步,创建default.aspx文件,用于提交表单时上传文件。
using system; namespace uploadhandler { public partial class uploadhandlerdefault : system.web.ui.page { protected void page_load(object sender, eventargs e) { string guid = request.params["guid"]; uploadutil utilhelp = new uploadutil(this, guid); utilhelp.upload(); } } }
上传核心代码:
using system; using system.web; using system.io; using system.configuration; using system.web.ui; using system.web.caching; using system.threading; public class uploadutil { private stream _reader; private filestream _fstream; private const int buffersize = 10000; private readonly string _filepath =new page().server.mappath(configurationmanager.appsettings["upload_folder"]); private readonly page _page; private readonly string _guid; public uploadutil(page page, string guid) { _page = page; _guid = guid; } public void upload() { if (_page.request.files.count > 0) { doupload(_page.request.files[0]); } } private void doupload(httppostedfile postedfile) { bool abort = false; string uploadfilepath = _filepath + datetime.now.tofiletime()+"//"; if (!directory.exists(uploadfilepath)) { directory.createdirectory(uploadfilepath); } string uploadfilename = postedfile.filename; downloadingfileinfo info = new downloadingfileinfo(uploadfilename, postedfile.contentlength, postedfile.contenttype); object fileobj = httpcontext.current.cache[_guid]; if (fileobj != null) { httpcontext.current.cache.remove(_guid); } httpcontext.current.cache.add(_guid, info, null, datetime.now.adddays(1), timespan.zero, cacheitempriority.abovenormal, null); datetime begin=datetime.now.tolocaltime(); _fstream = new filestream(uploadfilepath + uploadfilename, filemode.create); _reader = postedfile.inputstream; byte []buffer=new byte[buffersize]; int len = _reader.read(buffer,0,buffersize); while (len > 0&&!abort) { _fstream.write(buffer,0,len); datetime end = datetime.now.tolocaltime(); info.costtime = (long)(end - begin).totalmilliseconds; info.filefinished += len; //模拟延时用,实际应用的时候注销他 thread.sleep(1000); httpcontext.current.cache[_guid] = info; abort=((downloadingfileinfo)httpcontext.current.cache[_guid]).abort; len = _reader.read(buffer,0,buffersize); } _reader.close(); _fstream.close(); if (abort) { if (file.exists(uploadfilepath + uploadfilename)) { file.delete(uploadfilepath + uploadfilename); } } } }
第四步,创建handler.ashx文件,用于查看文件上传情况。
<%@ webhandler language="c#" class="progresshandler.handler" %> using system.web; using system.xml.linq; namespace progresshandler { public class handler : ihttphandler { /// <summary> /// 获得上传文件的进度 /// </summary> /// <param name="context">当前请求实体</param> /// <creattime>2015-06-28</creattime> /// <author>freshman</author> public void processrequest(httpcontext context) { context.response.contenttype = "application/xml"; context.response.charset = "utf-8"; var guid = context.request.form["guid"]; var info = context.cache[guid] as downloadingfileinfo; var doc = new xdocument(); var root = new xelement("root"); if (info != null) { var filename = new xelement("filename", info.filename); var filefinished = new xelement("filefinished", info.filefinished); var filesize = new xelement("filesize", info.filesize); var costtime = new xelement("costtime", info.costtime); var filestate = new xelement("filestate", info.filestate); var speed = new xelement("speed", info.speed); var percent = new xelement("percent", info.percent); var abort = new xelement("abort", false); root.add(filename); root.add(filefinished); root.add(filesize); root.add(costtime); root.add(filestate); root.add(speed); root.add(percent); if (info.abort) { abort.value = info.abort.tostring(); context.cache.remove(guid); } if (info.filestate == "finished") { context.cache.remove(guid); } } else { var none = new xelement("none", "no file"); root.add(none); } doc.add(root); context.response.write(doc.tostring()); context.response.end(); } public bool isreusable { get { return false; } } } }
第五步,创建abort.ashx文件,用于取消上传。
<%@ webhandler language="c#" class="progresshandler.abort" %> using system.web; using system.xml.linq; namespace progresshandler { public class abort : ihttphandler { /// <summary> /// 取消上传处理程序 /// </summary> /// <param name="context">当前请求实体</param> /// <creattime>2015-06-28</creattime> /// <author>freshman</author> public void processrequest(httpcontext context) { context.response.contenttype = "application/xml"; context.response.charset = "utf-8"; var guid = context.request.form["guid"]; var abort = !string.isnullorempty(context.request.form["abort"]); var info = context.cache[guid] as downloadingfileinfo; if (info != null) { info.abort = abort; context.cache[guid] = info; } var doc = new xdocument(); var root = new xelement("root"); var flag = new xelement("flag", info == null ? "false" : "true"); root.add(flag); doc.add(root); context.response.write(doc.tostring()); context.response.end(); } public bool isreusable { get { return false; } } } }
好了,下面就是编写javascript脚本了,我引用了jquery这个框架,另外还用了ui框架。
核心代码是ajax-progress-upload.js文件,另外还有一个获取guid的文件。
$(document).ready(function () { var _guid_url = "progresshandler/generateguid.ashx"; var _progress_url = "progresshandler/handler.ashx"; var _abort_url = "progresshandler/abort.ashx"; var _target = "#guid"; var _guid = ""; var _cancel = false; var _timer; ljq.setguid(_target, _guid_url); $("#upload_panel").draggable({ handle: "#upload_title" }); $("#upload_choose span").hover(function () { $(this).css({ "color": "#f6af3a", "border": "1px solid #e78f08" }); }, function () { $(this).css({ "color": "#1c94cd", "border": "1px solid #ddd" }); }); $("#upload_cancel").click(function () { $.ajax({ url: _abort_url, data: { guid: _guid, abort: true }, datatype: "xml", type: "post", success: function () { $("#upload_panel").fadeout('fast'); $("#back_panel").fadeout(1000); window.clearinterval(_timer); } }); }); $("#upload_submit").click(function () { $("#upload_panel").fadeout('fast'); $("#back_panel").fadeout("1000"); }); $("form").submit(function () { _guid = $(_target).val(); if ($("input[name='upload_file']").val() == "") { alert("未指定上传文件!"); return false; } $("#upload_progress").css("width", "0%"); $("#finished_percent").html("准备上传..."); $("#upload_speed").html(""); $("#upload_filename").html(""); $("#upload_filesize").html(""); $("#upload_costtime").html(""); var _option = { url: _progress_url, data: { guid: _guid }, datatype: "xml", type: "post", beforesend: function () { $("#back_panel").fadeto('fast', '0.5'); $("#upload_panel").fadein('1000'); }, success: function (response) { if ($(response).find("root abort").text() == "true") { $("#upload_panel").fadeout('fast'); $("#back_panel").fadeout(1000); window.clearinterval(_timer); } else if ($(response).find("root none").text() == "no file") { } else { var _percent = ($(response).find("root percent").text() * 100); var _speed = $(response).find("root speed").text(); var _filesize = $(response).find("root filesize").text(); var _upload_costtime = $(response).find("root costtime").text(); if (parseint(_speed) < 1024) { _speed = ljq.tofix(_speed) + "kb"; } else { _speed = ljq.tofix(_speed / 1024) + "mb"; } if (parseint(_filesize) / 1024 < 1024) { _filesize = ljq.tofix(_filesize / 1024) + "kb"; } else if (parseint(_filesize) / 1024 / 1024 < 1024) { _filesize = ljq.tofix(_filesize / 1024 / 1024) + "mb"; } else { _filesize = ljq.tofix(_filesize / 1024 / 1024 / 1024) + "gb"; } if (_upload_costtime < 1000) { _upload_costtime = _upload_costtime + "毫秒"; } else if (_upload_costtime / 1000 < 60) { _upload_costtime = parseint(_upload_costtime / 1000) + "秒" + _upload_costtime % 1000 + "毫秒"; } else { _upload_costtime = parseint(_upload_costtime / 1000 / 60) + "分" + parseint((_upload_costtime % 60000) / 1000) + "秒" + _upload_costtime % 1000 + "毫秒"; } $("#upload_progress").css("width", parseint(_percent) + "%"); $("#finished_percent").html("完成百分比:" + ljq.tofix(_percent) + "%"); $("#upload_speed").html("上传速度:" + _speed + "/sec"); $("#upload_filename").html("文件名称:" + $(response).find("root filename").text()); $("#upload_filesize").html("文件大小:" + _filesize); $("#upload_costtime").html("上传耗时:" + _upload_costtime); if (_percent >= 100) { window.clearinterval(_timer); $("#finished_percent").html("<span style='color:green;'>文件上传完成</span>"); } if (_cancel) { window.clearinterval(_timer); } } }, error: function () { } }; _timer = window.setinterval(function () { $.ajax(_option); }, 1000); }); });
以上为代码的主要部分。asp.net单文件带进度条上传,不属于任务控件,也不是flash类型的上传,完全是asp.net、js、css实现上传。源码为开发测试版,需要使用的亲需要注意修改配置文件。
项目源码下载请点击这里: