分享项目中在用的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;
}
}
分享项目中在用的asp.net下载业务的服务端基类(支持客户端显示下载百分比进度,支持并发数控制,支持限速)的更多相关文章
- VS2013中web项目中自动生成的ASP.NET Identity代码思考
vs2013没有再分webform.mvc.api项目,使用vs2013创建一个web项目模板选MVC,身份验证选个人用户账户.项目会生成ASP.NET Identity的一些代码.这些代码主要在Ac ...
- 关于项目中的一些经验:封装activity、service的基类,封装数据对象
经验一,将几个页面公用的数据,和方法进行封装,形成一个baseActivity的类: package com.ctbri.weather.control; import java.util.Array ...
- 使用ASP.Net WebAPI构建REST服务(五)——客户端
WebAPI是标准的Http协议,支持Http协议的客户端(如浏览器)都可以访问.但是,有的时候我们如果想在自己的程序中使用WebAPI时,此时就要实现自己的客户端了.我之前介绍过在.Net 4.5中 ...
- 项目案例【Net Core】如何注入多个服务实现类
需求 库表保存时,需要校验逻辑. 提交时有更深层次校验. **状态,还有特殊校验 接口 写一个通用的校验接口,这里定义了校验时间.每个阶段校验可能需要考虑顺序,增加一个顺序字段. public int ...
- okHttp,greenDao,EventBus组合框架项目中实战
okHttp,greenDao,EventBus组合封装 zzyandroid 介绍 开门见山,大体思路是在Activity中启动服务,通过服务创建Http请求,请求处理结果通过EventBus通知前 ...
- Xamarin.Froms项目中包含的文件
Clearly, the program created by the Xamarin.Forms template is very simple, so this is an excellent o ...
- Android项目中打jar包 和 使用
第一步,把普通的android project设置成库项目 库项目也是一个标准的android项目,因此你先创建一个普通的android项目. 这个项目可以起任何的名称,任何的报名,设置其他需要设置的 ...
- vue-cli项目中怎么mock数据
在vue项目中, mock数据可以使用 node 的 express模块搭建服务 1. 在根目录下创建 test 目录, 用来存放模拟的 json 数据, 在 test 目录下创建模拟的数据 data ...
- vue 项目中当访问路由不存在的时候默认访问404页面
前言: 在Vue项目中,当访问的页面路由不存在或错误时,页面显示为一片空白.然而,通常我们需要对访问url不存在或者错误的情况下添加默认的404页面,即not found页面. 一般的处理方法是: 在 ...
- salesforce零基础学习(八十八)项目中的零碎知识点小总结(二)
通过做项目以及群里面的一些大神的聊天,总结一下关于项目中的两个知识点,以后当做参考. 一. 在custom setting中配置集成接口信息后刷sandbox的问题 我们做项目时,经常会遇见和其他平台 ...
随机推荐
- IDEA清空控制台以及Java中运行cmd命令实现清屏操作
IDEA中清空控制台方法 在网上有看到各种的实现方法,比如: Runtime.getRuntime().exec("cls"); 或者: public static void cl ...
- PHP开启debug模式
我用的是lnmp一键集成环境 所以我的php.ini在/usr/local/php/ 把这个值改成从 Off 改成On 即可
- 7.pyagem-游戏背景
背景交替滚动 游戏启动后,背景图像不断的向下移动 在视觉上产生角色不断向上移动的错觉 游戏背景不断变化,游戏主角的位置报错不变 实现方案 创建两张背景图 第一张完全和屏幕重合,第二章在屏幕的正上方 ...
- C# RulesEngine 规则引擎:从入门到看懵
说明 RulesEngine 是 C# 写的一个规则引擎类库,读者可以从这些地方了解它: 仓库地址: https://github.com/microsoft/RulesEngine 使用方法: ht ...
- iOS开发应用上传AppStore的步骤
原文:http://blog.csdn.net/ayangcool/article/details/46647693 前言:作为一名IOS开发者,把开发出来的App上传到App Store是必须的 ...
- spalsh安装及简单使用
selenium是浏览器测试自动化工具,很容易完成鼠标点击,翻页等动作,确定是一次只能加载一个页面,无法异步渲染页面,也就限制了selenium爬虫的抓取效率. splash可以实现异步渲染页面,可以 ...
- 【实操日记】使用 PyQt5 设计下载远程服务器日志文件程序
最近通过 PyQt5 设计了一个下载服务器指定日期日志文件的程序,里面有些有意思的技术点,现在做一些分享. PyQt5 是一套 Python 绑定 Digia Qt5 应用的框架,是最强大的 GUI ...
- .Net6新版本的AssemblyLoadContext 加载程序集和卸载程序集
准备俩个项目 第一个是控制台 第二个项目是类库 类库项目中只有一个示例class 将类库的代码生成dll 并且设置属性为复制到输出目录 using System.Runtime.Loader; var ...
- 【深入浅出 Yarn 架构与实现】2-4 Yarn 基础库 - 状态机库
当一个服务拥有太多处理逻辑时,会导致代码结构异常的混乱,很难分辨一段逻辑是在哪个阶段发挥作用的. 这时就可以引入状态机模型,帮助代码结构变得清晰. 一.状态机库概述 一)简介 状态机由一组状态组成: ...
- Dockerfile 使用 SSH docker build
如果在书写 Dockerfile 时,有些命令需要使用到 SSH 连接,比如从私有仓库下载文件等,那么我们应该怎么做呢? Dockerfile 文件配置 为了使得 Dockerfile 文件中的命令可 ...