/// <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. 220722 T2 序列(ST表+分治)

    题目描述 小 B 喜欢玩游戏. 有一天,小 B 在玩一个序列上的游戏,他得到了正整数序列{ai}以及一个常数c . 游戏规则是,玩家可以对于每一个ai 分别加上一个非负整数x ,代价为 x2,完成所有 ...

  2. Java I/O(3):NIO中的Buffer

    您好,我是湘王,这是我的博客园,欢迎您来,欢迎您再来- 之前在调用Channel的代码中,使用了一个名叫ByteBuffer类,它是Buffer的子类.这个叫Buffer的类是专门用来解决高速设备与低 ...

  3. 构建Springboot项目、实现简单的输出功能、将项目打包成可以执行的JAR包(详细图解过程)

    1.构建SpringBoot项目 大致流程 1.新建工程 2.选择环境配置.jdk版本 3.选择 依赖(可省略.后续要手动在pom文件写) 4.项目名 1.1 图解建立过程 1.2 项目结构 友情提示 ...

  4. 实战进阶 Vue3+Axios+pinia

    实战进阶 Vue3+Axios+pinia 创建文件utils/request.js import Axios from 'axios'; export const request = Axios.c ...

  5. 走进shell

    走进shell 在Linux早起,还没有出现图形化,超哥和其他系统管理员都只能坐在电脑前,输入shell命令,查看控制台的文本输出. 在大多数Linux发行版里,例如centos,可以简单的用组合键来 ...

  6. 【Azure 环境】Azure 云环境对于OpenSSL 3.x 的严重漏洞(CVE-2022-3602 和 CVE-2022-3786)的处理公告

    问题描述 引用报告:(OpenSSL3.x曝出严重漏洞 : https://www.ctocio.com/ccnews/37529.html ) 最近OpenSSL 3.x 爆出了严重安全漏洞,分别是 ...

  7. 又拍云之 Keepalived 高可用部署

    在聊 Keepalived 之前,我们需要先简单了解一下 VRRP.VRRP(Virtual Router Redundancy Protocol)即虚拟路由冗余协议,是专门为了解决静态路由的高可用而 ...

  8. for in 和 for of 的区别和v-for指令的三种使用方法

    for...in 循环:只能获得对象的键名,不能获得键值 for...of 循环:允许遍历获得键值 var arr = ['red', 'green', 'blue'] for(let item in ...

  9. iOS逆向之某多多App抓包

    阅读此文档的过程中遇到任何问题,请关注公众号[移动端Android和iOS开发技术分享]或加QQ群[309580013] 1.目标 由于某多多App现使用longlink进行数据传输,使用charle ...

  10. win10+vs2019 编译webrtc m108

    不能访问外网途径的捷径 已经下载好的资源,可以直接生成工程: https://pan.baidu.com/s/14plvXZD_qX9nn441RbsCwA?pwd=ww8c 该资源可以跳过的步骤 步 ...