/// <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. PTA2022 520钻石争霸赛题解

    7-1 520表白 不用说 #include<bits/stdc++.h> using namespace std; typedef long long ll; const int max ...

  2. 洛谷P1640 SCOI2010 连续攻击游戏 (并查集/匹配)

    本题介绍两种做法: 1 并查集 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=1000005; 4 int ...

  3. AgileBoot - 基于SpringBoot + Vue3的前后端快速开发脚手架

    AgileBoot 仓库 后端地址:https://github.com/valarchie/AgileBoot-Back-End 技术栈:Springboot / Spring Security / ...

  4. <三>从编译器角度理解C++代码编译和链接原理

    1代码 点击查看代码 **sum.cpp** int gdata=10; int sum(int a,int b){ return a+b; } **main.cpp** extern int gda ...

  5. mysql安装教程-window操作系统

    1.下载安装包(官网下载) 直达链接:https://dev.mysql.com/downloads/mysql/ 下载后放到指定目录下解压即可(给电脑新手忠告:注意不要放在C盘,养成好习惯,放C盘多 ...

  6. Selenium+Python系列(三) - 常见浏览器操作

    写在前面 上篇文章为大家分享了自动化测试中,常见元素定位的操作. 今天再次读文章,居然忘记了大家特别喜欢的CSS和Xpath定位操作分享,这怎么能行呢? 马上安利,感兴趣的同学去参考下面链接: CSS ...

  7. Charles基本功能

    Windows: 运行安装应用程序以在程序菜单中安装 Charles. Mac OS X: 通过双击解压缩下载文件,然后将 Charles 应用程序复制到 Applications 目录中. Linu ...

  8. 基于.NetCore开发博客项目 StarBlog - (19) Markdown渲染方案探索

    前言 笔者认为,一个博客网站,最核心的是阅读体验. 在开发StarBlog的过程中,最耗时的恰恰也是文章的展示部分功能. 最开始还没研究出来如何很好的使用后端渲染,所以只能先用Editor.md组件做 ...

  9. spring框架-jdbcTemplate

    首先 dao层: dao -bookdao(interface) -bookdaoimpl service层: bookService 实体类对象 entiry-book 测试类 Test-TestB ...

  10. Spark简单介绍,Windows下安装Scala+Hadoop+Spark运行环境,集成到IDEA中

    一.前言 近几年大数据是异常的火爆,今天小编以java开发的身份来会会大数据,提高一下自己的层面! 大数据技术也是有很多: Hadoop Spark Flink 小编也只知道这些了,由于Hadoop, ...