/// <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. HDU1561 The more, The Better(树形背包)

    通过这道题对树形背包理解更深一步...... 有几个地方需要注意: 1.本题数据结构为森林,需增加一个超根作为根节点,M+=1(后面解释). 2.本题有拓扑序的限制,通过vector建成的一棵树中,必 ...

  2. Windows常用快捷键及基本的Dos命令

    Windows 常用快捷键 Ctrl + C: 复制 Ctrl + V: 粘贴 Ctrl + A: 全选 Ctrl + X: 剪贴 Ctrl + Z: 撤销 Ctrl + S: 保存 Alt + F4 ...

  3. Linux系统管理_磁盘管理——敬请期待!!!

    df 显示磁盘空间使用情况 -a 全部文件系统列表 -h 以方便阅读的方式显示 -T 列出文件系统类型 du 查看目录和文件的磁盘空间使用情况 -a 显示目录中所有文件大小 -h 以易读方式显示文件大 ...

  4. python和C语言从路径中获取文件名

    1.Python import os file_name = os.path.basename(filepath)#带后缀的文件名(不含路径) file_name_NoExtension = os.p ...

  5. JavaScript基础复盘补缺

    语法规范 JavaScript严格区分大小写,对空格.换行.缩进不敏感,建议语句结束加':' JavaScript 会忽略多个空格.您可以向脚本添加空格,以增强可读性. JavaScript 程序员倾 ...

  6. Workflow,要不要了解一下

    摘要:Workflow本质是开发者基于实际业务场景开发用于部署模型或应用的流水线工具. Workflow(也称工作流,下文中均可使用工作流进行描述)本质是开发者基于实际业务场景开发用于部署模型或应用的 ...

  7. 使用LabVIEW实现基于pytorch的DeepLabv3图像语义分割

    前言 今天我们一起来看一下如何使用LabVIEW实现语义分割. 一.什么是语义分割 图像语义分割(semantic segmentation),从字面意思上理解就是让计算机根据图像的语义来进行分割,例 ...

  8. JavaScrip基础学习笔记(一)

    一.三元表达式 1.1 什么是三元表达式 由三元运算符组成的式子我们称为三元表达式 1.2 语法结构 条件表达式 ? 表达式1 : 表达式2 1.3 执行思路 如果表达式为结果真 则返回表达式1的值, ...

  9. 2022春每日一题:Day 30

    题目:[JSOI2009]电子字典 读完题后,暴力?确实,计算一下时间复杂度最坏情况下,20263*10000=1.5e8,卡一下常可以直接卡到7e7,最严格来说应该卡的过去,但是此题数据可以直接卡过 ...

  10. Go语言核心36讲25

    你好,我是郝林,今天我分享的主题是:测试的基本规则和流程(上). 你很棒,已经学完了本专栏最大的一个模块!这涉及了Go语言的所有内建数据类型,以及非常有特色的那些流程和语句. 你已经完全可以去独立编写 ...