标签:断点 children ptr disk settings foreach 传递 url api
一、基本思想
利用 HTTP 请求的Range标头值,来向服务端点传递请求数据的开始位置和结束位置。服务端获得这两个参数后,将指定范围内的数据传递给客户端。当客户端请求暂停或中断之后吗,待到继续下载之时,便是开始按照一定的范围开始向服务器请求数据。
二、示例代码
1 DownloadCore:完成下载任务
public class DownloadCore<T> { private HttpRequestMessage request; private IFileProvider<T> fileProvider; private T requestModel; public DownloadCore(HttpRequestMessage request,IFileProvider<T> fileProvider,T requestModel) { this.request = request; this.fileProvider = fileProvider; this.requestModel = requestModel; } public HttpResponseMessage Download() { if (requestModel == null) { //抛出异常 } if (!fileProvider.Exists(requestModel)) { //抛出异常 } long fileLength = fileProvider.GetLength(requestModel); if (fileLength == 0) { //抛出异常 } ContentInfo contentInfo = GetContentInfoFromRequest(fileLength); Stream stream = PartialStream.GetPartialStream(fileProvider.Open(requestModel), contentInfo.From, contentInfo.To); if (stream == null) { //抛出异常 } HttpContent content = new StreamContent(stream, AppSettings.BufferSize); return SetResponse(content, contentInfo, fileLength, fileProvider.GetFileName(requestModel)); } private ContentInfo GetContentInfoFromRequest(long entityLength) { var result = new ContentInfo { From = 0, To = entityLength - 1, IsPartial = false, Length = entityLength }; RangeHeaderValue rangeHeader = request.Headers.Range; if (rangeHeader != null && rangeHeader.Ranges.Count != 0) { //仅支持一个range if (rangeHeader.Ranges.Count > 1) { //抛出异常 } RangeItemHeaderValue range = rangeHeader.Ranges.First(); if (range.From.HasValue && range.From < 0 || range.To.HasValue && range.To > entityLength - 1) { //抛出异常 } result.From = range.From ?? 0; result.To = range.To ?? entityLength - 1; result.IsPartial = true; result.Length = entityLength; if (range.From.HasValue && range.To.HasValue) { result.Length = range.To.Value - range.From.Value + 1; } else if (range.From.HasValue) { result.Length = entityLength - range.From.Value; } else if (range.To.HasValue) { result.Length = range.To.Value + 1; } } return result; } private HttpResponseMessage SetResponse(HttpContent content, ContentInfo contentInfo, long entityLength, string fileNameWithExtend) { HttpResponseMessage response = new HttpResponseMessage() ; response.Headers.AcceptRanges.Add("bytes"); response.StatusCode = contentInfo.IsPartial ? HttpStatusCode.PartialContent : HttpStatusCode.OK; response.Content = content; response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); response.Content.Headers.ContentDisposition.FileName = fileNameWithExtend; response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); response.Content.Headers.ContentLength = contentInfo.Length; if (contentInfo.IsPartial) { response.Content.Headers.ContentRange = new ContentRangeHeaderValue(contentInfo.From, contentInfo.To, entityLength); } return response; } }
网络请求参数解析
public static DownloadUriModel GetDownloadParam(HttpRequestMessage request) { var uriQuery = request.RequestUri.Query; //针对中文重新编码 uriQuery = HttpUtility.UrlDecode(uriQuery); var queryChildren = uriQuery.Substring(1, uriQuery.Length - 1).Split(new char[] { ‘&‘ }, StringSplitOptions.RemoveEmptyEntries).ToList(); Dictionary<string, string> queryDict = new Dictionary<string, string>(); queryChildren.ForEach(item => { var child = item.Split(new char[] { ‘=‘ }, StringSplitOptions.RemoveEmptyEntries); queryDict.Add(child[0], child[1]); }); string fileName = queryDict.ContainsKey("fileName") ? queryDict["fileName"] : null; if (string.IsNullOrWhiteSpace(fileName)) { //抛出异常 } string extend = Path.GetExtension(fileName); if (!string.IsNullOrWhiteSpace(extend)) { fileName = Path.GetFileNameWithoutExtension(fileName); } string fileType = queryDict.ContainsKey("fileType") ? queryDict["fileType"] : null; if (string.IsNullOrWhiteSpace(fileType)) { //抛出异常 } return new DownloadUriModel { FileNameNoExtend = fileName, FileType = fileType, }; }
部分流数据
public class PartialStream { public static Stream GetPartialStream(Stream fileStream, long start, long end) { if (fileStream == null) { return null; } if (start > 0) { fileStream.Seek(start, SeekOrigin.Begin); } return fileStream; } }
2 数据
下载数据的来源包括本地磁盘,网络,数据库等,这里只列举待下载数据在本地磁盘和网络的情形。
本地磁盘
public class DiskFileProvider : IFileProvider<DownloadRequestModel> { /// <summary> /// android app 所在文件夹路径 /// </summary> private readonly string filesDirectory; public DiskFileProvider() { filesDirectory = AppSettings.AppLocation; } public bool Exists(DownloadRequestModel model) { string searchPattern = string.Format("{0}.{1}",model.FileNameNoExtend, model.FileType); string file = Directory.GetFiles(filesDirectory, searchPattern, SearchOption.TopDirectoryOnly) .FirstOrDefault(); return file != null; } public Stream Open(DownloadRequestModel model) { return File.Open(GetFilePath(model), FileMode.Open, FileAccess.Read,FileShare.Delete); } public long GetLength(DownloadRequestModel model) { return new FileInfo(GetFilePath(model)).Length; } private string GetFilePath(DownloadRequestModel model) { string searchPattern = string.Format("{0}.{1}", model.FileNameNoExtend, model.FileType); return Path.Combine(filesDirectory, searchPattern); } public string GetFileName(DownloadRequestModel model) { return model.FileNameWithExtend; } }
网络
public class NetFileProvider: IFileProvider<DownloadUriModel> { public bool Exists(DownloadUriModel model) { //具体实现 } public Stream Open(DownloadUriModel model) { //具体实现 } ...... }
统一的接口
public interface IFileProvider<T> { bool Exists(T model); Stream Open(T model); long GetLength(T model); string GetFileName(T model); }
3 数据模型
public class DownloadRequestModel { /// <summary> /// 文件名(不含扩展名) /// </summary> public string FileNameNoExtend { get; set; } /// <summary> /// 文件类型(扩展名) /// </summary> public string FileType { get; set; } /// <summary> /// 文件名 /// </summary> public string FileNameWithExtend { get { string extend = FileType.Contains(".") ? FileType.Substring(1) : FileType; return string.Format("{0}.{1}", FileNameNoExtend, extend); } } } //扩展实现 public class DownloadUriModel:DownloadRequestModel { ...... } public class ContentInfo { public long From; public long To; public bool IsPartial; public long Length; }
标签:断点 children ptr disk settings foreach 传递 url api
原文地址:http://www.cnblogs.com/hdwgxz/p/7856409.html