前言

上一篇我们穿插了C#的内容,本篇我们继续来讲讲webapi中断点续传的其他情况以及利用webclient来实现断点续传,至此关于webapi断点续传下载以及上传内容都已经全部完结,一直嚷嚷着把SQL Server和Oracle数据库再重新过一遍,这篇过完,就要开始新的征程,每一个阶段都应该有自己的小目标,要不然当工作太忙没时间去充电,太闲又变得懒散,想想一切是为了未来买得起孩子高档的奶粉就又有动力了。

话题

关于webapi断点续传下载的情况,之前我们利用webapi内置的api展开了具体的实现,这一节我们利用已经老掉牙的技术来实现,这个是看了一篇老外文章而想到的,具体地址忘记了,利用内存映射文件来实现断点续传,内存映射文件最常见的应用场景莫过于对于多个进程之间共享数据,我们知道进程与进程之间只能操作已经分配好各自的内存,当我们需要一个进程与另外一个进程共享一块数据时我们该如何做呢,这个时候就要用到内存映射文件(MemoryMappedFile),内存映射文件是单一机器多进程间数据通信的最高效的方式,好了关于内存映射文件具体内容可以参考园友【.net 流氓】的文章。我们通过内存映射文件管理虚拟内存然后将其映射到磁盘上具体的文件中,当然我们得知道所谓的文件能够被映射并不是将文件复制到虚拟内存中,而是由于会被应用程序访问到,很显然windows会加载部分物理文件,通过使用内存映射文件我们能够保证操作系统会优化磁盘访问,此外我们能够得到内存缓存的形式。因为文件被映射到虚拟内存中,所以在管理大文件时我们需要在64位模式下运行我们的程序,否则将无法满足我们所需的所有空间。

断点续传(内存映射文件)

关于涉及到的类以及接口在之前文章已经叙述,这里我们就不再啰嗦,这里我们给出下载文件的逻辑。

        /// <summary>/// 下载文件/// </summary>/// <param name="fileName"></param>/// <returns></returns>public HttpResponseMessage GetFile(string fileName){if (!FileProvider.Exists(fileName)){throw new HttpResponseException(HttpStatusCode.NotFound);}long fileLength = FileProvider.GetLength(fileName);var fileInfo = GetFileInfoFromRequest(this.Request, fileLength);.........}

我们从请求信息中获取到了文件的信息,接下来我们就是利用内存映射文件的时候

 MemoryMappedFile.OpenExisting(mapName, MemoryMappedFileRights.Read);

自定义一个映射名称,若此时已存在我们则继续读打开的文件,若不存在我们将打开要下载的文件并创建内存映射文件。

mmf = MemoryMappedFile.CreateFromFile(FileProvider.Open(fileName), mapName, fileLength,MemoryMappedFileAccess.Read, null, HandleInheritability.None,false);

接着我们创建一个映射文件内存的视图流并返回最终将其写入到响应中的HttpContent中。

mmf.CreateViewStream(0, fileLength, MemoryMappedFileAccess.Read);response.Content = new StreamContent(stream);

整个利用内存映射文件下载文件的逻辑如下:

        /// <summary>/// 下载文件/// </summary>/// <param name="fileName"></param>/// <returns></returns>public HttpResponseMessage GetFile(string fileName){if (!FileProvider.Exists(fileName)){throw new HttpResponseException(HttpStatusCode.NotFound);}long fileLength = FileProvider.GetLength(fileName);var fileInfo = GetFileInfoFromRequest(this.Request, fileLength);var mapName = string.Format("FileDownloadMap_{0}", fileName);MemoryMappedFile mmf = null;try{mmf = MemoryMappedFile.OpenExisting(mapName, MemoryMappedFileRights.Read);}catch (FileNotFoundException){mmf = MemoryMappedFile.CreateFromFile(FileProvider.Open(fileName), mapName, fileLength,MemoryMappedFileAccess.Read, null, HandleInheritability.None,false);}using (mmf){Stream stream= fileInfo.IsPartial? mmf.CreateViewStream(fileInfo.From, fileInfo.Length, MemoryMappedFileAccess.Read): mmf.CreateViewStream(0, fileLength, MemoryMappedFileAccess.Read);var response = new HttpResponseMessage();response.Content = new StreamContent(stream);SetResponseHeaders(response, fileInfo, fileLength, fileName);return response;}}

有时候运行会出现如下错误:

再要么下载过程中出现访问遭到拒绝的情况

若将权限修改为 MemoryMappedFileAccess.ReadWrite ,也会出现访问遭到拒绝的情况,此二者错误的出现暂未找到解决方案,期待读者能给出一点见解或者答案。

断点续传(WebClient)

利用WebClient进行断点续传下载最主要的是对象 HttpWebRequest 中的AddRange方法,类似webapi中的RangeHeaderItemValue对象的from和to显示表明请求从哪里到哪里的数据。其余的就比较简单了,我们获取文件下载路径以及下载目的路径,下载过程中获取目的路径中文件的长度,若路径长度大于0则继续添加,小于0则从0创建文件并下载直到响应头中的文件长度即Content-Length和目的文件长度相等才下载完成。

第一步(打开Url下载)

 client.OpenRead(url);

第二步(若目标文件已存在,比较其余响应头中文件长度,若大于则删除重新下载)

                if (File.Exists(filePath)){var finfo = new FileInfo(filePath);if (client.ResponseHeaders != null &&finfo.Length >= Convert.ToInt64(client.ResponseHeaders["Content-Length"])){File.Delete(filePath);}}

第三步(断点续传逻辑)

            long existLen = 0;FileStream saveFileStream;if (File.Exists(destinationPath)){var fInfo = new FileInfo(destinationPath);existLen = fInfo.Length;}if (existLen > 0)saveFileStream = new FileStream(destinationPath,FileMode.Append, FileAccess.Write,FileShare.ReadWrite);elsesaveFileStream = new FileStream(destinationPath,FileMode.Create, FileAccess.Write,FileShare.ReadWrite);var httpWebRequest = (HttpWebRequest)System.Net.HttpWebRequest.Create(sourceUrl);   httpWebRequest.AddRange((int)existLen);var httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();using (var respStream = httpWebResponse.GetResponseStream()){var timout = httpWebRequest.Timeout;respStream.CopyTo(saveFileStream);}

第四步(判断目标文件长度与响应头中文件,相等则下载完成)

               fileInfo = fileInfo ?? new FileInfo(destinationFilePath);if (fileInfo.Length == Convert.ToInt64(client.ResponseHeaders["Content-Length"])){Console.WriteLine("下载完成.......");}else{throw new WebException("下载中断,请尝试重新下载......");}

整个利用WebClient下载逻辑如下:

(1)控制台调用,开始下载

            Console.WriteLine("开始下载......");try{DownloadFile("http://localhost:61567/FileLocation/UML.pdf", "d:\\temp\\uml.pdf");}catch (Exception ex){if (!string.Equals(ex.Message, "Stack Empty.", StringComparison.InvariantCultureIgnoreCase)){Console.WriteLine("{0}{1}{1} 出错啦: {1} {2}", ex.Message, Environment.NewLine,ex.InnerException.ToString());}}

(2)下载文件并判断下载是否完成

        public static void DownloadFile(string url, string filePath){var client = new WebClient();ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, errors) =>{ return true; };try{client.OpenRead(url);FileInfo fileInfo = null;if (File.Exists(filePath)){var finfo = new FileInfo(filePath);if (client.ResponseHeaders != null &&finfo.Length >= Convert.ToInt64(client.ResponseHeaders["Content-Length"])){File.Delete(filePath);}}DownloadFileWithResume(url, filePath);fileInfo = fileInfo ?? new FileInfo(destinationFilePath);if (fileInfo.Length ==        Convert.ToInt64(client.ResponseHeaders["Content-Length"])){Console.WriteLine("下载完成.......");}else{throw new WebException("下载中断,请尝试重新下载......");}}catch (Exception ex){Console.WriteLine("Error: {0} {1}", ex.Message, Environment.NewLine);Console.WriteLine("下载中断,请尝试重新下载......");throw;}}

(3)断点续传逻辑

        /// <summary>/// 断点续传下载/// </summary>/// <param name="sourceUrl"></param>/// <param name="destinationPath"></param>private static void DownloadFileWithResume(string sourceUrl, string destinationPath){long existLen = 0;FileStream saveFileStream;if (File.Exists(destinationPath)){var fInfo = new FileInfo(destinationPath);existLen = fInfo.Length;}if (existLen > 0)saveFileStream = new FileStream(destinationPath,FileMode.Append, FileAccess.Write,FileShare.ReadWrite);elsesaveFileStream = new FileStream(destinationPath,FileMode.Create, FileAccess.Write,FileShare.ReadWrite);var httpWebRequest = (HttpWebRequest)System.Net.HttpWebRequest.Create(sourceUrl);httpWebRequest.AddRange((int)existLen);var httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();using (var respStream = httpWebResponse.GetResponseStream()){var timout = httpWebRequest.Timeout;respStream.CopyTo(saveFileStream);}}

总结

至此在webapi中利用内存映射文件下载以及在控制台中利用WebClient下载叙述基本已经完结,其中或多或少还是存在一点问题,后续有时间再来看看,对于上述出现的问题,有解决方案的读者可以提供一下。接下来我将开始新的征程,开始SQL Server和Oracle数据库学习之旅。

更新

所有代码已经上传到右上角github,有需要请下载,或者直接点击如下地址clone:WebAPiResumeDownload

ASP.NET WebAPi之断点续传下载(下)相关推荐

  1. ASP.NET WebAPi之断点续传下载(中)

    前言 前情回顾:上一篇我们遗留了两个问题,一个是未完全实现断点续传,另外则是在响应时是返回StreamContent还是PushStreamContent呢?这一节我们重点来解决这两个问题,同时就在此 ...

  2. ASP.NET WebAPi之断点续传下载(上)

    前言 之前一直感觉断点续传比较神秘,于是想去一探究竟,不知从何入手,以为就写写逻辑就行,结果搜索一番,还得了解相关http协议知识,又花了许久功夫去看http协议中有关断点续传知识,有时候发觉东西只有 ...

  3. 带进度条的ASP无组件断点续传下载代码

    <%@LANGUAGE="VBSCRIPT" CODEPAGE="936"%> <%Option Explicit%> <% '= ...

  4. OkHttp实现多线程断点续传下载,单例模式下多任务下载管理器,一起抛掉sp,sqlite的辅助吧

    丨版权说明 :<OkHttp实现多线程断点续传下载,单例模式下多任务下载管理器,一起抛掉sp,sqlite的辅助吧>于当前CSDN博客和乘月网属同一原创,转载请说明出处,谢谢. 最近项目需 ...

  5. 【开源】分享一个前后端分离方案-前端angularjs+requirejs+dhtmlx 后端asp.net webapi

    一.前言 半年前左右折腾了一个前后端分离的架子,这几天才想起来翻出来分享给大家.关于前后端分离这个话题大家也谈了很久了,希望我这个实践能对大家有点点帮助,演示和源码都贴在后面. 二.技术架构 这两年a ...

  6. 【转】OData – the best way to REST–实例讲解ASP.NET WebAPI OData (V4) Service Client

    一.概念介绍 1.1,什么是OData? 还是看OData官网的简单说明: An open protocol to allow the creation and consumption of quer ...

  7. Winform宿主Asp.Net WebApi中Owin 自定义Token请求参数

    本文介绍的是Winform宿主Asp.net WebAPI的过程,利用了Microsoft.AspNet.WebApi.OwinSelfHost. Asp.NET WebAPI中的授权验证有很多: 例 ...

  8. ASP.NET WebApi OWIN 实现 OAuth 2.0

    OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. OAuth 允许用户提供一个令牌, ...

  9. asp.net webapi 自托管插件式服务(转)

    webapi问世已久,稀里糊涂的人哪它都当mvc来使,毕竟已mvc使用级别的经验就可以应对webapi. webapi和mvc在asp.net5时代合体了,这告诉我们,其实 它俩还是有区别的,要不现在 ...

最新文章

  1. 排序算法 - 堆排序
  2. 变量初始化赋值_java数组的定义及初始化
  3. cscript.exe自动启动解决方法分享
  4. jvm垃圾内存回收问题
  5. 吴恩达 coursera ML 第十五课总结+作业答案
  6. java为什么还需要分布式锁?
  7. 监督分类空白处也被分类了_监督学习(2)|本质是分类的“逻辑回归”
  8. 数据算法之二叉树删除(BinaryTreeL Remove)的Java实现
  9. 30天扣篮训练计划_明日之后:网易CH用心良苦?狼人画出“辐射高校30天计划”,绝了...
  10. 开始驻扎博客园,分享技术、学习心得与人生感悟,与大家一起成长
  11. python网易云_Python分析网易云音乐近5年热门歌单
  12. 关于程序设计大赛环境部署的WBS图
  13. 一般线性规划求最大值
  14. ArcGIS坡度分析(解决坡度分析80-89°高值居多)
  15. 一口气搞懂「文件系统」,就靠这 25 张图了
  16. 电脑重装Win10 64位系统的方法
  17. Python 使用SMTP协议发送邮件
  18. z-index 的最大值和最小值
  19. ssh-agent与ssh-sshd,开启ssh服务
  20. android u盘盘符乱码,分享U盘中文件出现乱码的原因以及解决方法

热门文章

  1. sqlce wp from查询语句详解
  2. 有感于框架设计难,实施框架更难!
  3. 跨平台PHP调试器设计及使用方法——立项
  4. FFmpeg中libavutil库简介及测试代码
  5. 主成分分析(PCA) C++ 实现
  6. travis-ci如何配置android
  7. Some Rules from wrting your own dsl with ruby
  8. java bufferedimage颜色_使用BufferedImage进行渐变色操作
  9. 怎么锁定电脑屏幕_和平精英怎么在电脑上玩?匹配手机最简单的方式玩你值得拥有...
  10. splitcontainer如何设置两边一样打_墙洞加筋如何计算?