• 分享项目中在用的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;
            }
        }

      

  • 相关阅读:
    视频智能分析系统EasyCVR视频流媒体安防监控云服务实现城市视频智能化应用
    Windows/Android/iOS平台H265编码视频播放器EasyPlayerRTSP中socket选项之keepalive设置介绍
    【解决方案】人脸识别/车牌识别RTSP/GB28181/SDK/Ehome协议视频平台EasyCVR搭建美丽乡村视频监控系统方案
    【操作说明】新版网络穿透+云端组网+远程运维+视频流拉转推平台EasyNTS上云网关管理平台如何安装?
    Windows/Android/iOS平台H265编码视频播放器EasyPlayerPro支持高码率视频播放和D3D画面旋转代码参考
    【操作说明】人脸识别/车牌识别系统视频智能分析平台EasyCVR如何配置开启HTTPS协议?
    程序员们 不要想一辈子靠技术混饭吃
    jsonObject的使用
    程序员们 不要想一辈子靠技术混饭吃
    从 iBatis 到 MyBatis
  • 原文地址:https://www.cnblogs.com/taohuadaozhu/p/13530016.html
Copyright © 2020-2023  润新知