码迷,mamicode.com
首页 > Windows程序 > 详细

asp.net web api 下载之断点续传

时间:2017-11-18 13:43:55      阅读:274      评论:0      收藏:0      [点我收藏+]

标签:断点   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;
}

 

asp.net web api 下载之断点续传

标签:断点   children   ptr   disk   settings   foreach   传递   url   api   

原文地址:http://www.cnblogs.com/hdwgxz/p/7856409.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!