asp.net支持断点下载
作者:翅膀的初衷 来源:本站原创 发布时间:2013-10-08 查看数:62052
当文件比较大时,下载可能需要历时数小时,万一线路中断,我们不得不重新开始,而断点续传可以让用户从上次断开的地方接着下载,但是断点续传功能不仅仅需要客户端的支持,也需要服务端的支持,本文演示如何让asp.net支持断点下载!
这是我整理文件时,找出来的4年前的代码,原理很清晰,就是代码有点乱,大家将就看看!
using System;
using System.IO;
using System.Web;
using System.Text;
using System.Threading;
namespace FuncUtility
{
/*
* 0 未开始下载
* 1 正在下载中
* 2 下载完成
* 3 下载失败
* 4 文件不存在
* 5 文件过大
* 6 文件已更新 与上次下载不同
* 7 续传错误
* 8 程序错误
* 9 未初始化
*/
public class DownLoad
{
private HttpRequest _httpRequest;
private HttpResponse _httpResponse;
private string _filePath;
private long _speed;
private int _status;
private int _packSize;
/// <summary>
/// HttpRequest
/// </summary>
public HttpRequest httpRequest
{
set { _httpRequest = value; }
}
/// <summary>
/// HttpResponse
/// </summary>
public HttpResponse httpResponse
{
set { _httpResponse = value; }
}
/// <summary>
/// 文件路径(物理路径)
/// </summary>
public string FilePath
{
set { _filePath = value; }
}
/// <summary>
/// 每秒充许下载的字节数
/// </summary>
public long Speed
{
set { _speed = value; }
}
/// <summary>
/// 状态
/// </summary>
public int Status
{
get { return _status; }
}
/// <summary>
/// 分块下载大小
/// </summary>
public int PackSize
{
set { _packSize = value; }
}
public DownLoad()
{
_status = 0;
//_httpContext = null;
//_filePath = null;
_speed = 20480;
_packSize = 10240;
}
public DownLoad(HttpRequest httpreques, HttpResponse httpresponse, string path)
{
//默认下载速度20KB/秒 每块10KB
_status = 0;
_httpResponse = httpresponse;
_httpRequest = httpreques;
_filePath = path;
_speed = 20480;
_packSize = 10240;
}
public DownLoad(HttpRequest httpreques, HttpResponse httpresponse, string path, long speed, int packSize)
{
_status = 0;
_httpResponse = httpresponse;
_httpRequest = httpreques;
_filePath = path;
_speed = speed;
_packSize = packSize;
}
public void Start()
{
if (!string.IsNullOrEmpty(_filePath) && _httpRequest != null && _httpResponse!=null)
DownloadFile();
else
_status = 9;
}
private void DownloadFile()
{
if (!File.Exists(_filePath))
{
_status = 4;
_httpResponse.StatusCode = 404;
}
else
{
//开始下载
_status = 1;
//开始字节数
long startBytes = 0;
//获取文件名
string fileName = Path.GetFileName(_filePath);
//以只读方式读取文件流
FileStream fso = new FileStream(_filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
BinaryReader br = new BinaryReader(fso);
long fileLength = fso.Length;
//毫秒数:读取下一数据块的时间间隔
int sleep = (int)Math.Ceiling(1000.0 * _packSize / _speed);
//文件修改时间 UTC
string lastUpdateTiemStr = File.GetLastWriteTimeUtc(_filePath).ToString("r");
//便于恢复下载时提取请求头;
string eTag = HttpUtility.UrlEncode(fileName, Encoding.UTF8) + lastUpdateTiemStr;
//判断文件是否太大
if (fso.Length > Int32.MaxValue)
{
_httpResponse.StatusCode = 413;//请求实体太大
_status = 5;
}
else
{
bool _chang = true;
//对应响应头ETag:文件名+文件最后修改时间
if (_httpRequest.Headers["If-Range"] != null)
{
//上次被请求的日期之后被修改过
if (_httpRequest.Headers["If-Range"].Replace("\"", "") != eTag)
{
//文件修改过 无法下载
_chang = false;
_status = 6;
//预处理失败
_httpResponse.StatusCode = 412;
}
}
if (_chang)
{
try
{
_httpResponse.Clear();
_httpResponse.Buffer = false;
//_httpResponse.AddHeader("Content-MD5", GetMD5Hash(myFile));//用于验证文件
_httpResponse.AddHeader("Accept-Ranges", "bytes");//重要:续传必须
_httpResponse.AppendHeader("ETag", string.Concat("\"",eTag,"\""));//重要:续传必须
_httpResponse.AppendHeader("Last-Modified", lastUpdateTiemStr);//把最后修改日期写入响应
_httpResponse.ContentType = "application/octet-stream";//MIME类型:匹配任意文件类型
_httpResponse.AddHeader("Content-Disposition",string.Concat("attachment;filename=",HttpUtility.UrlEncode(fileName, Encoding.UTF8).Replace("+", "%20")));
_httpResponse.AddHeader("Content-Length", (fileLength - startBytes).ToString());
_httpResponse.AddHeader("Connection", "Keep-Alive");
_httpResponse.ContentEncoding = Encoding.UTF8;
//如果是续传
if (_httpRequest.Headers["Range"] != null)
{
//重要:续传必须,表示局部范围响应。初始下载时默认为200
_httpResponse.StatusCode = 206;
string[] range = _httpRequest.Headers["Range"].Split(new char[] { '=', '-' });
//已下载字符串,续传起始位置
startBytes = Convert.ToInt64(range[1]);
//判断起始位置是否有误
if (startBytes < 0 || startBytes >= fileLength)
{
_status = 7;
_chang = false;
}
}
if (_chang)
{
//如果是续传请求,告诉客户端本次的开始字节数,总长度,以便客户端将续传数据追加到startBytes位置后
if (startBytes > 0)
_httpResponse.AddHeader("Content-Range", string.Format(" bytes {0}-{1}/{2}", startBytes, fileLength - 1, fileLength));
//向用户发送数据
br.BaseStream.Seek(startBytes, SeekOrigin.Begin);
//分块下载,剩余部分可分成的块数
int maxCount = (int)Math.Ceiling((fileLength - startBytes + 0.0) / _packSize);
//发送数据
for (int i = 0; i < maxCount && _httpResponse.IsClientConnected; i++)
{
_httpResponse.BinaryWrite(br.ReadBytes(_packSize));
_httpResponse.Flush();
if (sleep > 1)
Thread.Sleep(sleep);
}
}
}
catch
{
_status = 8;
}
finally
{
br.Close();
fso.Close();
fso.Dispose();
}
}
}
}
}
}
}