Asp.Net 無刷新文件上傳並顯示進度條的實現方法及思路

相信經過Asp.Net的服務器控件上傳文件在簡單不過了,經過AjaxToolkit控件實現上傳進度也不是什麼難事,爲何還要本身辛辛苦苦來 實現呢?我並不否定」拿來主義「,只是我我的更喜歡凡是求個因此然。本篇將闡述經過Html,IHttpHandler和 IHttpAsyncHandler實現文件上傳和上傳進度的原理,但願對你有多幫助。javascript

效果圖:css

本文涉及到的知識點:
1.前臺用到Html,Ajax,JQuery,JQuery UIjava

2.後臺用到通常處理程序(IHttpHandler)和通常異步處理程序(IHttpAsyncHandler),並涉及到」推模式「jquery

1、建立Html網頁
一、在建立的Web工程中添加一個Html文件,命名爲UploadFile.htm,在頭文件中引入JQuery,JQuery UIweb

<link href="Styles/jquery-ui-1.8.16.custom.css" rel="stylesheet" type="text/css" />
    <script src="Scripts/jquery-1.6.2.min.js" type="text/javascript"></script>
    <script src="Scripts/jquery-ui-1.8.16.custom.min.js" type="text/javascript"></script>數組

二、關於無刷新文件上傳服務器

 

經過Ajax是不能上傳文件的,無刷新上傳是靠隱藏的iframe來實現的併發

<form id="form" target = "frameFileUpload" enctype="multipart/form-data">
<div id="progressBar" style="font-size: 1em;"></div>
<input type="file" id="fileUpload" name="fileUpload" /><span id="progressValue"></span>
<iframe id="frameFileUpload" name="frameFileUpload" style="display:none;" ></iframe>
<br />
<input type="submit" value="上傳" id = "submit"/>
</form>異步

要將form標籤的target屬性設置爲iframe的id,固然別忘了將form的enctype設置爲multipart/form-data函數

<div id="progressBar" style="font-size: 1em;"></div>

是用來顯示上傳文件時的進度條

 

在JS中加入以下處理:

 <script type="text/javascript">
        $(function () {
            $("#submit").button();
            $("#fileUpload").button();
        });
    </script>

此時效果:

2、實現文件上傳
添加一個通常處理程序,命名爲UploadFileHandler.ashx

  public void ProcessRequest(HttpContext context)
        {
            //若是提交的文件名是空,則不處理
            if (context.Request.Files.Count == 0 || string.IsNullOrWhiteSpace(context.Request.Files[0].FileName))
                return;
            //獲取文件流
            Stream stream = context.Request.Files[0].InputStream;
            //獲取文件名稱
            string fileName = Path.GetFileName(context.Request.Files[0].FileName);
            //聲明字節數組
            byte[] buffer;
            //爲何是4096呢?這是操做系統中最小的分配空間,若是你的文件只有100個字節,其實它佔用的空間是4096個字節
            int bufferSize = 4096;
            //獲取上傳文件流的總長度
            long totalLength = stream.Length;
            //已經寫入的字節數,用於作上傳的百分比
            long writtenSize = 0;
            //建立文件
            using (FileStream fs = new FileStream(@"C:\" + fileName, FileMode.Create, FileAccess.Write))
            {
                //若是寫入文件的字節數小於上傳的總字節數,就一直寫,直到寫完爲止
                while (writtenSize < totalLength)
                {
                    //若是剩餘的字節數不小於最小分配空間
                    if (totalLength - writtenSize >= bufferSize)
                    {
                        //用最小分配空間建立新的字節數組
                        buffer = new byte[bufferSize];
                    }
                    else
                        //用剩餘的字節數建立字節數組
                        buffer = new byte[totalLength - writtenSize];
                    //讀取上傳的文件到字節數組
                    stream.Read(buffer, 0, buffer.Length);
                    //將讀取的字節數組寫入到新建的文件流中
                    fs.Write(buffer, 0, buffer.Length);
                    //增長寫入的字節數
                    writtenSize += buffer.Length;
                    //計算當前上傳文件的百分比
                    long percent = writtenSize * 100 / totalLength;
                }
            }
        }

在form中添加action和method屬性,修改以後的

<form action="UploadFileHandler.ashx" method="post" id="form" target = "frameFileUpload" enctype="multipart/form-data">

這樣文件上傳就完成了。

 

3、實現文件上傳的進度顯示
個人思路:

  文件上傳的處理過程當中,是不能夠在處理過程當中將信息傳回客戶端的,只有當全部的處理都完畢以後纔會傳回客戶端,因此若是是在上面的處理程序中寫 入context.Response.Write(percent);是不可能獲得處理的過程,只能等處處理結束後,客戶端一次性獲得全部的值。

  要想獲得處理過程當中的值,個人解決是這樣,在文件上傳時,要開啓另外一個請求,來獲取進度信息。而這個請求是異步的,我指的是客戶端異步請求和服 務端異步處理。由於要涉及到兩個不一樣的請求處理程序之間信息的傳遞,將"處理文件上傳的程序"獲得的進度信息傳遞給"處理進度請求的程序",而"處理進度 請求的處理程序"要依賴於"處理文件上傳的處理程序"。處理圖:

首先客戶端同時(幾乎是)發出兩個請求,一個是文件上傳,一個是進度請求。因爲"處理請求進度的程序"是異步處理的,當該程序沒有信息發給客戶 端時,咱們讓它處於等待狀態,這裏有點像Tcp,這樣客戶端跟服務器就一直處於鏈接狀態。當"處理文件上傳的程序"開始處理時,經過把進度值賦值給"處理 請求進度程序"的異步操做的狀態,並觸發"處理請求進度的程序"返回值給客戶端。客戶端獲取進度值,並處理。這樣一次請求進度值的請求就結束了,咱們知道 服務器是不會主動給客戶端發送信息的,只有客戶端請求,服務器纔會響應。顯然,要想在文件保存的過程當中向客戶端發送進度信息,客戶端獲得每獲得一個返回結 果,都是一次請求。爲了獲得連續的請求值,客戶端再向"處理請求進度的程序"發出請求,依次循環,知道文件上傳結束。

技術實現:
  異步處理用到接口IHttpAsyncHandler,新建一個通常處理程序,命名爲RequestProgressAsyncHandler.ashx,將默認的接口改成IHttpAsyncHandler

 public class RequestProgressAsyncHandler : IHttpAsyncHandler
    {
        public void ProcessRequest(HttpContext context)
        {
        }
        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
        #region IHttpAsyncHandler 成員
        public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
        {
            throw new NotImplementedException();
        }
        public void EndProcessRequest(IAsyncResult result)
        {
            throw new NotImplementedException();
        }
        #endregion
    }

BeginProcessRequest和EndProcessRequest是兩個核心的方法,其餘的兩個不用處理。當該處理程序處理請求 時,BeginProcessRequest是第一個被調用的函數,返回一個包含異步狀態信息的對象,該對象是IAsyncResult類型,是實現異步 的關鍵,用於控制何時調用EndProcessRequest來結束處理程序的等待狀態,BeginProcessRequest被調用以後,程序就 處於等待狀態。EndProcessRequest是在結束請求時的處理函數,經過該函數能夠向客戶端寫入信息。

 

實現接口IAsyncResult

  public class AsyncResult : IAsyncResult
    {
        // 標示異步處理的狀態
        private bool isComplete = false;

 

        //保存異步處理程序中的Http上下文
        private HttpContext context;

        //異步回調的委託
        private AsyncCallback callback;
        /// <summary>
        /// 獲取或設置保存下載文件的百分比數值部分
        /// </summary>
        public long PercentNumber;

        public AsyncResult(HttpContext context, AsyncCallback callback)
        {
            this.context = context;
            this.callback = callback;
        }
        /// <summary>
        /// 向客戶端寫入信息
        /// </summary>
        public void Send()
        {
            this.context.Response.Write(PercentNumber);
        }
        /// <summary>
        /// 完成異步處理,結束請求
        /// </summary>
        public void DoCompleteTask()
        {
            if (callback != null)
                callback(this);//會觸發處理程序中的EndProcessRequest函數,結束請求
            this.isComplete = true;
        }
        #region IAsyncResult 成員

        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 isComplete; }
        }

        #endregion

    }

修改 RequestProgressAsyncHandler.ashx文件:

 public class RequestProgressAsyncHandler : IHttpAsyncHandler
    {
        /// <summary>
        /// 保存異步處理狀態信息的集合
        /// </summary>
        public static List<AsyncResult> AsyncResults = new List<AsyncResult>();
        public void ProcessRequest(HttpContext context)
        {
        }
        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
        #region IHttpAsyncHandler 成員

 

        public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
        {

            AsyncResult result = new AsyncResult(context, cb);
            AsyncResults.Add(result);
            return result;
        }

        public void EndProcessRequest(IAsyncResult result)
        {
            //保證集合中只用一個元素
            AsyncResults.Clear();
            AsyncResult ar = (AsyncResult)result;
            ar.Send();
        }

        #endregion
    }

在UploadFileHandler.ashx添加以下代碼:

  private static void SendPercentToClient(long percent)
        {
            //當上傳完畢後,保證處理程序能向客戶端傳回
            while (RequestProgressAsyncHandler.AsyncResults.Count == 0 && percent == 100)
            {

 

            }
            //由於本處理程序和"處理請求進度的程序"是併發的,不能保證RequestProgressAsyncHandler.AsyncResults必定含有子項
            if (RequestProgressAsyncHandler.AsyncResults.Count != 0)
            {
                RequestProgressAsyncHandler.AsyncResults[0].PercentNumber = percent;
                RequestProgressAsyncHandler.AsyncResults[0].DoCompleteTask();
            }
        }

在函數ProcessRequest中加入以上方法:


             ...
                     ...
             //計算當前上傳文件的百分比
                    long percent = writtenSize * 100 / totalLength;

 

                    SendPercentToClient(percent);

服務端OK!修改客戶端,添加JS處理函數:

   function RequestProgress() {
            $.post("RequestProgressAsyncHandler.ashx", function (data, status) {
                if (status == "success") {
                    $("#progressValue").text(data + "%");
                    data = parseInt(data);
                    $("#progressBar").progressbar({ value: data });//JQuery UI 設置進度條值
                    //若是進度不是 100,則從新請求
                    if (data != 100) {
                        RequestProgress();
                    }
                }
            });
        }

在form中添加事件omsubmit的處理函數爲RequestProgress

<form action="UploadFileHandler.ashx" onsubmit = "RequestProgress();" method="post" id="form" target = "frameFileUpload" enctype="multipart/form-data">

補充幾點:
1.默認Asp.Net容許的上傳文件的大小是4M,能夠在Web.config中修改其大小限制

  <system.web>
        <httpRuntime maxRequestLength="444444"/>
    </system.web>

maxRequestLength的單位是KB

相關文章
相關標籤/搜索