在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
在日常工作中,有时候需要到远程服务器上部署新版本的系统,由于远程服务器出于外网,所以每次都要开QQ连接,非常麻烦。索性就研究了下IHttpasyncHandler,并结合Juqery ProgressBar,打造了一款大文件传送器。其基本原理就是首先在客户端将大文件分段拆分,然后写入内存流,最后发送到服务器上。在上传的同时,会利用异步Handler来获取当前进度并推送到前台显示。图示效果如下(当前缓存设置为5000字节大小): 图示结果 (图1,传送到36%的时候) (图2,传送到100%的时候)
异步Handler设计 下面来说说具体的实现方式,首先来说说实时通知这块: using System; using System.Collections.Generic; using System.Web; using AsyncThermometerDeamon.Handlers.Code; namespace AsyncThermometerDeamon.Handlers { public class FileUploadWatcher : IHttpAsyncHandler { public static List<AsyncResultDaemon> asyncResults = new List<AsyncResultDaemon>(); public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) { AsyncResultDaemon asyncResult = new AsyncResultDaemon(context,cb,extraData); asyncResults.Add(asyncResult); return asyncResult; } public void EndProcessRequest(IAsyncResult result) { asyncResults.Clear(); AsyncResultDaemon aResult = result as AsyncResultDaemon; aResult.Send(); } public bool IsReusable { get { return false; } } public void ProcessRequest(HttpContext context) { throw new NotImplementedException(); } } } 在程序中,首先申明了一个List容器,以便保存当前的请求。然后当有请求进来的时候,BeginProcessRequest会将当前请求进行初始化,然后加入到List容器中,最后将执行结果返回。 而在EndProcessRequest函数中,一旦当前的操作完成,将会触发此函数推送当前的进度数据到前台。 这里我们来看一下AsyncResultDaemon类: using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace AsyncThermometerDeamon.Handlers.Code { public class AsyncResultDaemon:IAsyncResult { public AsyncResultDaemon(HttpContext context, AsyncCallback cb,object extraData) { this.context = context; this.cb = cb; } private HttpContext context; private AsyncCallback cb; private object extraData; public long percent; public bool isCompleted = false; public void Send() { context.Response.Write(percent); } public void CompleteTask() { if (cb != null) { cb(this); this.isCompleted = true; } } public object AsyncState { get { return null; } } public System.Threading.WaitHandle AsyncWaitHandle { get { return null; } } public bool CompletedSynchronously { get { return false; } } public bool IsCompleted { get { return isCompleted; } } } }
文件上传Handler设计 说完了异步Handler,再来说一说文件上传Handler: using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.IO; namespace AsyncThermometerDeamon.Handlers { public class FileUpload : IHttpHandler { //设置发送的缓冲大小 private int bufferSize = 5000; public void ProcessRequest(HttpContext context) { //得到文件全路径及文件名称 string filePath = context.Request.QueryString["path"]; string fileName = Path.GetFileName(filePath); byte[] byteToSend; FileStream fs = new FileStream(filePath, FileMode.Open); fs.Position = 0; long totalBytesLength = fs.Length; //文件分多少批发送 long totalBatch = 0; if (totalBytesLength % bufferSize == 0) totalBatch = totalBytesLength / bufferSize; else totalBatch = totalBytesLength / bufferSize + 1; //遍历 for (int i = 0; i < totalBatch; i++) { //设置当前需要获取的流的位置 fs.Position = i * bufferSize; //如果是最后一批流数据 if (i == totalBatch - 1) { long lastBatchLength = totalBytesLength = totalBytesLength - i * bufferSize; byteToSend = new byte[lastBatchLength]; fs.Read(byteToSend, 0, (int)lastBatchLength); } else { byteToSend = new byte[bufferSize]; fs.Read(byteToSend, 0, bufferSize); } //避免闭包 int j = i; //写数据 using (FileStream fsWrite = new FileStream(@"C:\" + fileName, FileMode.Append, FileAccess.Write)) { fsWrite.Write(byteToSend, 0, byteToSend.Length); fsWrite.Flush(); } //预报当前发送状态 long percentage = (j+1)*100/totalBatch; //while循环能够保证最后一批数据顺利推送到前台 while (FileUploadWatcher.asyncResults.Count == 0 && percentage == 100) { } if (FileUploadWatcher.asyncResults.Count != 0) { FileUploadWatcher.asyncResults[0].percent = percentage; FileUploadWatcher.asyncResults[0].CompleteTask(); } } fs.Close(); } public bool IsReusable { get { return false; } } } }
文件上传这块,主要是通过将大文件分割,然后放到一个容积为5000字节的缓冲区中,分段发送,以避免出现OutOfMemory的错误。所以,这种处理机制可以保证发送大数据的文件,比如说1GB大小的文件。在每次写文件的时候,程序会利用long percentage = (j+1)*100/totalBatch;来计算当前的进度,并且通过如下的代码来将当前的进度数据进行推送: while (FileUploadWatcher.asyncResults.Count == 0 && percentage == 100) { } if (FileUploadWatcher.asyncResults.Count != 0) { FileUploadWatcher.asyncResults[0].percent = percentage; FileUploadWatcher.asyncResults[0].CompleteTask(); } 如果当前有请求,那么就可以调用异步Handler中的CompleteTask来报告本次的进度,CompleteTask将会抛出事件来触发EndProcessRequest,EndProcessRequest则会将当前的进度数据推送至前台。 While循环主要是保证最后一次数据推送能够正常完成,去掉这句,数据一直会显示完成度为99%。 前台设计 最后来看看前台设计吧: -//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>文件上传</title> <style type="text/css"> body{font-size:12px;} #txtPath{border:none;border-bottom:1px solid black;width:300px;} #fileUpload{border:none;border-bottom:1px solid black;width:250px;background-color:White;height:25px;} #btnUpload{border:none;background:url(Image/btn_01.gif) no-repeat;width:100px;height:30px;} #result {width:400px;} </style> <link href="Styles/jquery-ui-1.8.16.custom.css" rel="stylesheet" type="text/css" /> <script type="text/javascript" src="Scripts/jquery-1.6.4.min.js"></script> <script src="Scripts/jquery-ui-1.8.16.custom.min.js" type="text/javascript"></script> <script type="text/javascript"> $(document).ready(function () { $("#btnUpload").bind("click", function () { StartFileUpload(); TriggerAjax(); }); }); var StartFileUpload = function () { var filePath = $("#fileUpload").val(); var handerUrl = "Handlers/FileUpload.ashx?path=" + filePath; $.ajax({ url: handerUrl, type: "GET", success: function (result) {}, error: function (result) {} }); } var TriggerAjax = function () { var handerUrl = "Handlers/FileUploadWatcher.ashx"; $.ajax({ url: handerUrl, type: "GET", success: function (result) { var data = parseInt(result); $("#result").progressbar({ value: data }); $("#percent").text(data+"%"); if (result != 100) { TriggerAjax(); } }, error: function (result) { debugger; alert(result); } }); } </script> </head> <body> <form > 请输入目标路径:<asp:TextBox ID="txtPath" runat="server" Text="\\192.168.0.180\MatiSoftDaemon\TestUploading" ></asp:TextBox> <br /> <br /> 请选择本地文件:<asp:FileUpload ID="fileUpload" runat="server" /> <input /> <br /> <div ></div> <div ></div> </form> </body> </html> 前台代码很简单,就是通过StartFileUpload()函数触发文件上传动作, TriggerAjax()函数触发进度检测动作。 源代码 感谢本文提供的灵感:Asp.Net实现无刷新文件上传并显示进度条(非服务器控件实现)
|
请发表评论