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

分享项目中在用的asp.net下载业务的服务端基类(支持客户端显示下载百分比进度,支持并发数控制,支持限速)的更多相关文章

  1. VS2013中web项目中自动生成的ASP.NET Identity代码思考

    vs2013没有再分webform.mvc.api项目,使用vs2013创建一个web项目模板选MVC,身份验证选个人用户账户.项目会生成ASP.NET Identity的一些代码.这些代码主要在Ac ...

  2. 关于项目中的一些经验:封装activity、service的基类,封装数据对象

    经验一,将几个页面公用的数据,和方法进行封装,形成一个baseActivity的类: package com.ctbri.weather.control; import java.util.Array ...

  3. 使用ASP.Net WebAPI构建REST服务(五)——客户端

    WebAPI是标准的Http协议,支持Http协议的客户端(如浏览器)都可以访问.但是,有的时候我们如果想在自己的程序中使用WebAPI时,此时就要实现自己的客户端了.我之前介绍过在.Net 4.5中 ...

  4. 项目案例【Net Core】如何注入多个服务实现类

    需求 库表保存时,需要校验逻辑. 提交时有更深层次校验. **状态,还有特殊校验 接口 写一个通用的校验接口,这里定义了校验时间.每个阶段校验可能需要考虑顺序,增加一个顺序字段. public int ...

  5. okHttp,greenDao,EventBus组合框架项目中实战

    okHttp,greenDao,EventBus组合封装 zzyandroid 介绍 开门见山,大体思路是在Activity中启动服务,通过服务创建Http请求,请求处理结果通过EventBus通知前 ...

  6. Xamarin.Froms项目中包含的文件

    Clearly, the program created by the Xamarin.Forms template is very simple, so this is an excellent o ...

  7. Android项目中打jar包 和 使用

    第一步,把普通的android project设置成库项目 库项目也是一个标准的android项目,因此你先创建一个普通的android项目. 这个项目可以起任何的名称,任何的报名,设置其他需要设置的 ...

  8. vue-cli项目中怎么mock数据

    在vue项目中, mock数据可以使用 node 的 express模块搭建服务 1. 在根目录下创建 test 目录, 用来存放模拟的 json 数据, 在 test 目录下创建模拟的数据 data ...

  9. vue 项目中当访问路由不存在的时候默认访问404页面

    前言: 在Vue项目中,当访问的页面路由不存在或错误时,页面显示为一片空白.然而,通常我们需要对访问url不存在或者错误的情况下添加默认的404页面,即not found页面. 一般的处理方法是: 在 ...

  10. salesforce零基础学习(八十八)项目中的零碎知识点小总结(二)

    通过做项目以及群里面的一些大神的聊天,总结一下关于项目中的两个知识点,以后当做参考. 一. 在custom setting中配置集成接口信息后刷sandbox的问题 我们做项目时,经常会遇见和其他平台 ...

随机推荐

  1. 一个C#开发者学习SpringCloud搭建微服务的心路历程

    前言 Spring Cloud很火,很多文章都有介绍如何使用,但对于我这种初学者,我需要从创建项目开始学起,所以这些文章对于我的启蒙,帮助不大,所以只好自己写一篇文章,用于备忘. SpringClou ...

  2. spring boot集成redis基础入门

    redis 支持持久化数据,不仅支持key-value类型的数据,还拥有list,set,zset,hash等数据结构的存储. 可以进行master-slave模式的数据备份 更多redis相关文档请 ...

  3. The XOR Largest Pair (trie树)

    题目描述 在给定的 NN 个整数 A_1,A_2,--,A_NA1​,A2​,--,AN​ 中选出两个进行xor运算,得到的结果最大是多少?xor表示二进制的异或(^)运算符号. 输入格式 第一行输入 ...

  4. 配置DNS域名解析服务

    概:   DNS技术作为互联网基础设施中的重要一环,为用户提供不间断.稳定且快速的域名查询服务,保证互联网正常运转.在互联网中,用户基本上都是基于DNS服务,使用域名访问网络上的计算机,DNS服务是我 ...

  5. 使用 Kubeadm 部署 K8S安装

    1. 安装要求 在开始之前,部署Kubernetes集群机器需要满足以下几个条件: 一台或多台机器,操作系统 CentOS7.x-86_x64 硬件配置:2GB或更多RAM,2个CPU或更多CPU,硬 ...

  6. MySQL高可用集群MHA方案

    MySQL高可用集群MHA方案 爱奇艺在用的数据库高可用方案 MHA 是目前比较成熟及流行的 MySQL 高可用解决方案,很多互联网公司正是直接使用或者基于 MHA 的架构进行改造实现 MySQL 的 ...

  7. maven-入门到入土

    详情见代码重工:连接http://heavy_code_industry.gitee.io/code_heavy_industry/pro002-maven/

  8. Spring Boot 中使用 Swagger

    前后端分离开发,后端需要编写接⼝说明⽂档,会耗费⽐较多的时间. swagger 是⼀个⽤于⽣成服务器接⼝的规范性⽂档,并且能够对接⼝进⾏测试的⼯具. 作用 ⽣成接⼝说明⽂档 对接⼝进⾏测试 使用步骤 ...

  9. python中展示json数据不换行(手动换行)

    https://blog.csdn.net/chichu261/article/details/82784904 Settings ->keymap -> 在搜索框输入 wraps -&g ...

  10. Byte和byte的区别

    Byte和byte的区别 背景 今天学习网络编程中,在建立Udp连接时,使用byte[]数组接收传输的数据,但是byte[]错写为Byte[],导致错误.    //接收数据:         Byt ...