大文件上傳之異常處理(轉載)

 最近一個項目需要用到HTTP文件上傳功能,做過類似功能的朋友都知道,實現HTTP文件上傳的功能並不難,使用ASP.NET就更加方便了,服務器端控件HtmlInputFile提供了強大的支持。一切進展得很順利,功能很快就實現了,套用電視劇《美麗的田野》裏那位週期性出現的傢伙的一個詞:一切看上去都十分“美觀”。

但是當嘗試上傳較大的文件時,一切似乎變得“醜陋”起來—程序崩潰,而且出現“The page cannot be displayed”的錯誤頁面。查閱相關資料後得知ASP.NET中默認的上傳文件的最大值爲4M,在web.config中可進行設置,但是即使設得再大,用戶上傳的文件都有可能超過限制。於是想到在Global.asax中的Application_Error事件處理過程中捕獲“Maximum request length exceeded.”異常然後使頁面Redirect至Custom Error Page。然而這個方法也行不通,雖然在Application_Error中可以捕獲到“Maximum request length exceeded.”異常,但是頁面無法Redirect至Custom Error Page,依舊是“The page cannot be displayed”。

事情一下變得棘手起來,很多技術文章也說這是HTTP協議爲保證web服務器的穩定及安全所設的限制。就連微軟官方的Support上也寫着:“When the maxRequestLength attribute is set in the Machine.config file and then a request is posted (for example, a file upload) that exceeds the value of maxRequestLength, a custom error page cannot be displayed. Instead, Microsoft Internet Explorer will display a "Cannot find server or DNS" error message.”這看似一個受協議限制的無解問題。正當我打算就此作罷之際,無意間看到一片文章,根據其上所寫的方案,這個問題居然解決了,真是山窮水復疑無路,柳暗花明又一村。

現將該方案按照我的思路整理出來,一來做一下總結,二來希望能對他人有所幫助。這固然有事後孔明之嫌,但畢竟還是好處多多。

前面說過,等Application_Error事件處理捕獲到“Maximum request length exceeded.”異常爲時已晚,頁面已無法正常Redirect至Custom Error Page,因此應該設法使系統不拋出“Maximum request length exceeded.”異常。而要使系統不拋出該異常,就應在HttpHandler處理Request之前,使HttpHandler得到的HttpContext的內容小於maxRequestLength。看來,可以在Global.asax中的Application_BeginRequest事件處理過程中作一番文章。下面的代碼就給出瞭解決方案:

    void Application_BeginRequest(Object source, EventArgs e)
    {
        #region 上傳文件超過限制
        HttpRequest request = HttpContext.Current.Request;
        if (request.ContentLength > 41943040)//40M
        {
            HttpApplication app = source as HttpApplication;
            HttpContext context = app.Context;

            HttpWorkerRequest wr = (HttpWorkerRequest)(context.GetType().GetProperty("WorkerRequest", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(context, null));
            byte[] buffer;
            if (wr.HasEntityBody())
            {
                int contentlen = Convert.ToInt32(wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderContentLength));
                buffer = wr.GetPreloadedEntityBody();
                int received = buffer.Length;
                int totalrecv = received;
                if (!wr.IsEntireEntityBodyIsPreloaded())
                {
                    buffer = new byte[65535];
                    while (contentlen - totalrecv >= received)
                    {
                        received =wr.ReadEntityBody(buffer,buffer.Length);
                        totalrecv += received;
                    }
                    received =wr.ReadEntityBody(buffer, contentlen - totalrecv);
                }
            }
           
            string url=Common.Common.ApplicationVirtualPath + "Error.aspx?msg="+context.Server.UrlEncode("上傳文件總大小超過限制!");
            context.Response.Redirect(url, true);
           
        }
        #endregion
    }

從代碼可以看出,關鍵的思路就是:如果上傳的文件超過maxRequestLength,則上傳文件已可以看作無效文件,於是就可以放心大膽的爲其瘦身,使Request的HttpContext的內容小於maxRequestLength,這樣“Maximum request length exceeded.”異常就不會再產生,頁面也就可以正常跳轉到Custom Error Page了。代碼中的循環部分就是將上傳的文件內容讀入buffer中,因爲上傳文件已屬於無效文件,所以文件內容也就不重要,這裏buffer的用途僅僅是用來接收上傳文件內容,buffer中的數據也無需再被讀取。

一個本來看似無解的問題就這樣解決了,雖然憑的不是一己之力,但仍然收穫頗豐,所以感覺自然仍是十分“美觀”!

 

 轉載地址:http://www.cnblogs.com/2bno1/archive/2005/11/28/285893.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章