Http的Range
请求头,结合响应头Accept-Ranges
、Content-Range
可以实现如下功能:
1、断点续传。用于下载文件被中断后,继续下载。可避免已下载的部分重复下载。
2、大文件指定区块下载。如视频、音频拖动播放,直接定位到指定位置下载内容。可避免每次都读取、传输整个文件,从而提升服务端性能。
3、大文件分包批量下载,再合并为完整文件。可提高下载速度。
Http的Range相关说明:
1、Header格式:
- 请求头:
Range: bytes=START-END
- 响应头:
Accept-Ranges: bytes
- 响应头:
Content-Range: bytes=START-END
2、规则要点
- 请求头
Range
表示请求的数据起始位置。响应头Accept-Ranges: bytes
表示支持续传。响应头Content-Range
表示返回的起始位置、总长度。 - 请求头
Range
的数字,首尾都包含,长度是:end - begin + 1 - 请求头
Range
指定的长度,只是意向下载量,服务端不一定要返回请求的长度。比如:bytes=0-
,表示希望下载整个文件,但服务端可以只返回有限长度的数据块(有利于性能)。但数据起始位置start需按照请求。
3、规则参考:https://tools.ietf.org/html/r...
4、Range参数解析
Http "Range" header:
The first 500 bytes (byte offsets 0-499, inclusive):
Range: bytes=0-499
The second 500 bytes (byte offsets 500-999, inclusive):
Range: bytes=500-999
Additional examples, assuming a representation of length 10000, The final 500 bytes (byte offsets 9500-9999, inclusive):
Range: bytes=-500
Or:
Range: bytes=9500-
话不多说,直接上代码:
using System;
using System.Configuration;
using System.IO;
using System.Net;
// 需要续传的下载页面,从这个类继承即可
public class DownloadPage : System.Web.UI.Page
{
static int HttpRangeSize = 3 * 1024 * 1024; //断点续传的最大区块长度, 默认3M
protected void WriteFile(string fn)
{
using (var fs = File.OpenRead(fn))
{
WriteStream(fs);
}
}
protected void WriteStream(Stream fs)
{
var range = (Request.Headers["Range"] ?? "").Trim().ToLower();
//请求包含了Range要求,并且源数据流支持定位(如FileStream、MemoryStream,网络流一般不支持),才进行续传
if (fs.CanSeek && range.StartsWith("bytes") && range.Contains("-"))
{
int start = -1, end = -1;
var rgs = range.Substring(6).Split('-');
int.TryParse(rgs[0], out start);
int.TryParse(rgs[1], out end);
if (rgs[0] == "") // bytes=-500
{
start = (int)fs.Length - end;
end = (int)fs.Length - 1;
}
if (rgs[1] == "") // bytes=9500-
{
end = (int)fs.Length - 1;
}
WriteRangeStream(fs, start, end);
}
else
{
WriteAllStream(fs);
}
}
private void WriteAllStream(Stream fs)
{
using (fs)
{
int l;
byte[] buffer = new byte[40960];
while ((l = fs.Read(buffer, 0, buffer.Length)) > 0)
{
Response.OutputStream.Write(buffer, 0, l);
}
}
}
//断点续传的附件下载,如视频
private void WriteRangeStream(Stream fs, int start, int end)
{
using (fs)
{
int rangeLen = end - start + 1;
if (rangeLen > 0)
{
if (rangeLen > HttpRangeSize) //每段默认限制最大3M
{
rangeLen = HttpRangeSize;
end = start + HttpRangeSize - 1;
}
}
else
{
throw new Exception("Range error");
}
long size = -1;
try
{
size = fs.Length;
}
catch { }
//如果是整个文件,返回200。否则返回206
var status = start == 0 && end + 1 >= size ? HttpStatusCode.OK : HttpStatusCode.PartialContent;
Response.StatusCode = (int)status;
Response.AddHeader("Accept-Ranges", "bytes");
//Content-Range形式:【bytes 起始位置-结束位置/总长度】。注意前置的bytes+空格不能少!
Response.AddHeader("Content-Range", string.Format("bytes {0}-{1}/{2}", start, end, size == -1 ? "*" : size.ToString()));
//Content-Length非必须。.Net也会自动生成。对批量分包下载有帮助
//Response.AddHeader("Content-Length", rangeLen.ToString());
int readLen, total = 0;
byte[] buffer = new byte[40960];
//流定位到指定位置
fs.Seek(start, SeekOrigin.Begin);
while (total < rangeLen && (readLen = fs.Read(buffer, 0, buffer.Length)) > 0)
{
total += readLen;
if (total > rangeLen) //超出的长度,截取
{
readLen -= (total - rangeLen);
total = rangeLen;
}
Response.OutputStream.Write(buffer, 0, readLen);
}
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。