核心代码
public static class DownloadHelper
{
/// <summary>
/// 断点下载
/// </summary>
/// <param name="controller"></param>
/// <param name="fullpath"></param>
/// <returns></returns>
public static async Task<long> RangeDownload(this Controller controller, string fullpath,string filename)
{
long size, start, end, length, fp = 0;
using (StreamReader reader = new StreamReader(File.OpenRead(fullpath)))
{
size = reader.BaseStream.Length;
start = 0;
end = size - 1;
length = size;
controller.Response.Headers.Add("Accept-Ranges", "0-" + size);
if (!String.IsNullOrEmpty(controller.Request.Headers["Range"]))
{
long anotherStart = start;
long anotherEnd = end;
string[] arr_split = controller.Request.Headers["Range"].FirstOrDefault().Split(new char[] { Convert.ToChar("=") });
string range = arr_split[1];
if (range.IndexOf(",") > -1)
{
controller.Response.Headers.Add("Content-Range", "bytes " + start + "-" + end + "/" + size);
controller.Response.StatusCode = 416;
}
if (range.StartsWith("-"))
{
anotherStart = size - Convert.ToInt64(range.Substring(1));
}
else
{
arr_split = range.Split(new char[] { Convert.ToChar("-") });
anotherStart = Convert.ToInt64(arr_split[0]);
long temp = 0;
anotherEnd = (arr_split.Length > 1 && Int64.TryParse(arr_split[1].ToString(), out temp)) ? Convert.ToInt64(arr_split[1]) : size;
}
anotherEnd = (anotherEnd > end) ? end : anotherEnd;
if (anotherStart > anotherEnd || anotherStart > size - 1 || anotherEnd >= size)
{
controller.Response.Headers.Add("Content-Range", "bytes " + start + "-" + end + "/" + size);
controller.Response.StatusCode = 416;
await controller.Response.WriteAsync("Requested Range Not Satisfiable");
}
start = anotherStart;
end = anotherEnd;
length = end - start + 1;
fp = reader.BaseStream.Seek(start, SeekOrigin.Begin);
controller.Response.StatusCode = 206;
}
}
// Notify the client the byte range we‘ll be outputting
controller.Response.Headers.Add("Content-Range", "bytes " + start + "-" + end + "/" + size);
controller.Response.Headers.Add("Content-Length", length.ToString());
controller.Response.Headers.Add("Content-Disposition", $"attachment; filename={filename}") ;
// Start buffered download
await controller.Response.SendFileAsync(fullpath, fp, length);
return fp;
}
}