.net c# 文件分片/断点续传--服务端
说起这个断点续传,一开始只是为了实现 pdf.js 的快速预览!在pc 端谷歌chrome 和火狐浏览器可以通过以下用法实现 pdf.js 的快速预览,无需完整下载整个pdf. 主要参考了 这篇文章 https://blog.csdn.net/niedewang/article/details/79883828,但是这篇文章没有提到服务端怎么实现(如果文件路径是网站的路径,服务器会自动支持断点续传,但是如果是我们写代码实现的下载,需要手动实现断点续传)。
断点续传实现主要参考了两位博主的文章:
https://blog.csdn.net/binyao02123202/article/details/76599949
https://www.cnblogs.com/CreateMyself/p/6063646.html
主要主要注意的地方:
1.通过判断是否包含 “Range” 请求头来确认是否断点续传(分片)的请求,如果包含则说明是,则从 “Range” 请求头的值获取范围。
2.返回的请求头要包含“Accept-Ranges”,"Content-Range"客户端才能判断服务是否支持断点续传。
3.返回的数据片段需要手动进行分片!上面也提到了主要参考两位博主的文章,其中第一篇那里,让我陷入了一个误区,以为系统会自动分片。
4.服务端的一般处理流程:
a.判断是否是分片请求(带 Range 请求头)
b.是分片请求,则根据 Range 的范围给出数据片。(如果收到 的range 为 0-,一般情况会返回整个流,但还是建议返回一小段数据)
c.不是分片请求,则返回整个流
以下是实现代码(有些地方用到了 vs2017 才支持的语法,如放到旧版本 vs 报错还需要改过来才行):
1.mvc 实现
public class File2Controller : Controller
{
private string filePath = @"D:\Adobe_Dreamweaver_CS5-jfsky.rar";
public ActionResult Index()
{
return View();
}
// GET: File2
public void Download()
{
//前面可以做用户登录验证、用户权限验证等。
string filename = Path.GetFileName(filePath); //客户端保存的文件名
//string filePath = Server.MapPath("/App_Data/大数据.rar");//要被下载的文件路径
var range = Request.Headers["Range"];
if (!string.IsNullOrWhiteSpace(range))//如果遵守协议,支持断点续传
{
var fileLength = new FileInfo(filePath).Length;//文件的总大小
long begin;//文件的开始位置
long end;//文件的结束位置
long.TryParse(range.Split('=')[1].Split('-')[0], out begin);
long.TryParse(range.Split('-')[1], out end);
end = end - begin > 0 ? end : 65536>fileLength-1? fileLength-1: 65536;// 如果没有结束位置,默认给一个大小
var buffer = new byte[end-begin+1];
using (var fileStream = System.IO.File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
fileStream.Seek(begin, SeekOrigin.Begin);
fileStream.Read(buffer, 0, buffer.Length);
}
//表头 表明 下载文件的开始、结束位置 和文件总大小
Response.AddHeader("Content-Range", "bytes " + begin + "-" + end + "/" + fileLength);
Response.Headers.Add("Content-Length", buffer.Length.ToString());
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition", "attachment;filename=" + filename);
Response.OutputStream.Write(buffer, 0, buffer.Length);//发送 文件开始位置读取的大小
Response.Flush();
Response.End();
}
else
{
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition", "attachment;filename=" + filename);
Response.TransmitFile(filePath);
}
}
}
2.webapi 实现
public class FileController : ApiController
{
private string filePath = @"G:\XLNetAccSetup.exe";
[HttpGet]
[HttpPost]
public IHttpActionResult Download()
{
var browser = String.Empty;
if (HttpContext.Current.Request.UserAgent != null)
{
browser = HttpContext.Current.Request.UserAgent.ToUpper();
}
HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK);
FileStream fileStream = File.OpenRead(filePath);
httpResponseMessage.Content = new StreamContent(fileStream);
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
httpResponseMessage.Headers.AcceptRanges.Add("bytes");
httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = browser.Contains("FIREFOX") ? Path.GetFileName(filePath) : HttpUtility.UrlEncode(Path.GetFileName(filePath))
};
return ResponseMessage(httpResponseMessage);
}
[HttpGet]
[HttpPost]
public IHttpActionResult Download2()
{
var range = Request.Headers.Range?.Ranges?.FirstOrDefault();
if (range == null) return Download();
var browser = String.Empty;
if (HttpContext.Current.Request.UserAgent != null)
{
browser = HttpContext.Current.Request.UserAgent.ToUpper();
}
HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpStatusCode.PartialContent);
FileStream fileStream = File.Open(filePath,FileMode.Open, FileAccess.Read, FileShare.Read);
long fromIndex = range.From.Value;
long toIndex = 0;
if (range.To == null )
{
toIndex = fromIndex + 60000;
if (toIndex > fileStream.Length-1) toIndex = fileStream.Length-1;
}
else
{
toIndex = range.To.Value;
}
var count = fileStream.Length;
var memoryStream = new MemoryStream();
var buffer = new byte[toIndex- fromIndex+1];
using (fileStream)
{
fileStream.Seek(fromIndex, SeekOrigin.Begin);
fileStream.Read(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, buffer.Length);
memoryStream.Position = 0;
httpResponseMessage.Content = new StreamContent(memoryStream);
}
//httpResponseMessage.Content = new StreamContent(fileStream,80*1024);
httpResponseMessage.Content.Headers.ContentRange = new ContentRangeHeaderValue(fromIndex, toIndex, count);
httpResponseMessage.Content.Headers.ContentLength = memoryStream.Length;
httpResponseMessage.Headers.AcceptRanges.Add("bytes");
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = browser.Contains("FIREFOX") ? Path.GetFileName(filePath) : HttpUtility.UrlEncode(Path.GetFileName(filePath))
};
return ResponseMessage(httpResponseMessage);
}
}
上一篇: java文件上传