分享項目中在用的asp.net下載業務的服務端基類(支持客戶端顯示下載百分比進度,支持併發數控制,支持限速)

    /// <summary>
    /// 功能簡介:asp.net的下載業務的服務端基類(支持客戶端顯示下載百分比進度,支持併發數控制,支持限速)
    /// 建立時間:2015-11-20
    /// 建立人:pcw
    /// 博客:https://www.cnblogs.com/taohuadaozhu
    /// 備註:若是針對大文件下載,則還須要考慮操做系統或iis上最大下載字節數限制。
    /// </summary>
    public abstract class DownLoadAbs : IHttpHandler
    {
        private static StatusDataDict currStatuDataDict = new StatusDataDict(300);
        protected object lockObj = new object();
        public virtual void ProcessRequest(HttpContext context)
        {
            string sDiplayFileName = this.GetDisplayFileName(context);
            string sServerFileFullPath = this.GetServerFileFullPath(context);
            int iDownload = 0;
            iDownload = this.ResponseFile(context.Request, context.Response, sDiplayFileName, sServerFileFullPath, this.BytesCountPerSecond);
            if (iDownload != 1)
            {
                Utils.SaveErrorLog(string.Format("下載文件【{0}】失敗(2015-12-15v1),返回值={1}", sServerFileFullPath, iDownload));
                if (iDownload == -202)
                {
                    context.Response.Write(RuntimeContext.GetResponseJson("系統檢測到重複的併發下載請求,請稍後再點擊下載", -1, null));
                }
                else if (iDownload == -203)
                {
                    context.Response.Write(RuntimeContext.GetResponseJson("併發下載人數超過最大鏈接數,請稍後再點擊下載", -2, null));
                }
                context.Response.End();
            }
        }
        protected abstract string GetDisplayFileName(HttpContext hcontext);
        protected abstract string GetServerFileFullPath(HttpContext hcontext);

        protected virtual int GetMaxConnectCount()
        {
            return 3;
        }


        protected virtual long BytesCountPerSecond
        {
            get
            {
                return 1024000;
            }
        }
        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
        /// <summary>
        /// 輸入參數 _Request: Page.Request對象,  _Response: Page.Response對象, _fileName: 下載文件名, _fullPath: 帶文件名下載路徑, _speed 每秒容許下載的字節數(默認:1024000 B,相似1M/秒)
        /// </summary>
        /// <param name="_Request"></param>
        /// <param name="_Response"></param>
        /// <param name="_displayFileName"></param>
        /// <param name="_serverFilefullPath"></param>
        /// <param name="_speed"></param>
        /// <returns></returns>
        protected int ResponseFile(HttpRequest _Request, HttpResponse _Response, string _displayFileName, string _serverFilefullPath, long _speed)
        {
            return this.ResponseForDownloadFile(_Request, _Response, _displayFileName, _serverFilefullPath, _speed);
        }
        /// <summary>
        /// 輸入參數 _Request: Page.Request對象,  _Response: Page.Response對象, _fileName: 下載文件名, _fullPath: 帶文件名下載路徑, _speed 每秒容許下載的字節數(默認:1024000 B,相似1M/秒)
        /// </summary>
        /// <param name="_Request"></param>
        /// <param name="_Response"></param>
        /// <param name="_displayFileName"></param>
        /// <param name="_serverFilefullPath"></param>
        /// <param name="_speed"></param>
        /// <returns></returns>
        protected virtual int ResponseForDownloadFile(HttpRequest _Request, HttpResponse _Response, string _displayFileName, string _serverFilefullPath, long _speed)
        {
            bool bSuccess = true;
            if (string.IsNullOrEmpty(_serverFilefullPath))
                return -101;
            if (string.IsNullOrEmpty(_displayFileName))
                return -102;
            if (_speed < 1)
                return -103;
            if (_Request == null)
                return -104;
            if (_Response == null)
                return -105;
            if (File.Exists(_serverFilefullPath) == false)
                return -201;
            if (currStatuDataDict.ExistsStatus(_serverFilefullPath))
            {
                return -202;
            }
            if (currStatuDataDict.GetStatusCount() >= this.GetMaxConnectCount())
            {
                return -203;
            }
            currStatuDataDict.AddStatusData(_serverFilefullPath);
            FileStream targetFile = new FileStream(_serverFilefullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            BinaryReader br = new BinaryReader(targetFile);
            try
            {
                _Response.AddHeader("Accept-Ranges", "bytes");
                _Response.Buffer = false;
                long fileTotalLength = targetFile.Length;
                long startBytes = 0;
                int packForBlock = 10240; //10K bytes
                //int sleep = 200;   //每秒5次   即5*10K bytes每秒
                decimal dSleep = Convert.ToDecimal(1000 * packForBlock / _speed);
                decimal dMaxCount = 0;
                int sleep = (int)Math.Floor(dSleep) + 1;
                if (_Request.Headers["Range"] != null) //這裏是客戶端返回來的,已下載的進度
                {
                    _Response.StatusCode = 206;
                    string[] range = _Request.Headers["Range"].Split(new char[] { '=', '-' });
                    startBytes = Convert.ToInt64(range[1]);
                }
                _Response.AddHeader("Content-Length", (fileTotalLength - startBytes).ToString());//這是這次下載文件的總字節長度
                if (startBytes != 0)//若是客戶端支持,不然不會添加進度相關的信息
                {
                    _Response.AddHeader("Content-Range", string.Format(" bytes {0}-{1}/{2}", startBytes, fileTotalLength - 1, fileTotalLength));//這是本次下載後從新定位的進度
                }
                /*
                _Response.AddHeader("Connection", "Keep-Alive");
                _Response.AddHeader("Keep-Alive", "timeout=600, max=4");
                 */
                _Response.ContentType = "application/octet-stream";
                _Response.AddHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(_displayFileName, System.Text.Encoding.UTF8));
                br.BaseStream.Seek(startBytes, SeekOrigin.Begin);
                dMaxCount = (fileTotalLength - startBytes) / packForBlock;
                int maxCount = (int)Math.Floor(dMaxCount) + 1;
                byte[] bytesRead = new byte[packForBlock];
                for (int i = 0; i < maxCount; i++)
                {
                    if (_Response != null && _Response.IsClientConnected)
                    {
                        if (File.Exists(_serverFilefullPath))
                        {
                            bytesRead = br.ReadBytes(packForBlock);
                            if (bytesRead != null)
                            {
                                _Response.BinaryWrite(bytesRead);
                                //_Response.Flush();//add by pcw 
                                Thread.Sleep(sleep);//須要注意響應的最大時間設置
                            }
                        }
                    }
                    else
                    {
                        i = maxCount;
                    }
                }
            }
            catch (Exception error)
            {
                bSuccess = false;
                Utils.SaveErrorLog(string
.Format("輸出文件【{0}】的文件流過程出現異常:{1},調試信息:{2}", _serverFilefullPath, error.Message, error.StackTrace));
            }
            finally
            {
                currStatuDataDict.RemoveStatuData(_serverFilefullPath);
                if (br != null)
                {
                    br.Close();
                    br.Dispose();
                    br = null;
                }
                if (targetFile != null)
                {
                    targetFile.Close();
                    targetFile.Dispose();
                    targetFile = null;
                }
                if (_Response != null)
                {
                    if (bSuccess)
                    {
                        Utils.SaveLog(string.Format("已成功提供客戶端下載文件【{0}】", _serverFilefullPath));
                    }
                    //_Response.End();
                    HttpContext.Current.Response.SuppressContent = true;  // Gets or sets a value indicating whether to send HTTP content to the client.
                    HttpContext.Current.ApplicationInstance.CompleteRequest(); // Causes ASP.NET to bypass all events and filtering in the HTTP pipeline chain of execution and directly execute the EndRequest event.
                }
            }
            return 1;
        }
    }
相關文章
相關標籤/搜索