ASP.NET WebAPi之断点续传下载(下)
前言
上一篇我们穿插了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(, 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(, 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 = ;
FileStream saveFileStream;
if (File.Exists(destinationPath))
{
var fInfo = new FileInfo(destinationPath);
existLen = fInfo.Length;
}
if (existLen > )
saveFileStream = new FileStream(destinationPath,
FileMode.Append, FileAccess.Write,
FileShare.ReadWrite);
else
saveFileStream = 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 = ;
FileStream saveFileStream;
if (File.Exists(destinationPath))
{
var fInfo = new FileInfo(destinationPath);
existLen = fInfo.Length;
}
if (existLen > )
saveFileStream = new FileStream(destinationPath,
FileMode.Append, FileAccess.Write,
FileShare.ReadWrite);
else
saveFileStream = 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之断点续传下载(下)的更多相关文章
- ASP.NET WebAPi之断点续传下载(中)
前言 前情回顾:上一篇我们遗留了两个问题,一个是未完全实现断点续传,另外则是在响应时是返回StreamContent还是PushStreamContent呢?这一节我们重点来解决这两个问题,同时就在此 ...
- ASP.NET WebAPi之断点续传下载(上)
前言 之前一直感觉断点续传比较神秘,于是想去一探究竟,不知从何入手,以为就写写逻辑就行,结果搜索一番,还得了解相关http协议知识,又花了许久功夫去看http协议中有关断点续传知识,有时候发觉东西只有 ...
- NET WebAPi之断点续传下载1
ASP.NET WebAPi之断点续传下载(上) 前言 之前一直感觉断点续传比较神秘,于是想去一探究竟,不知从何入手,以为就写写逻辑就行,结果搜索一番,还得了解相关http协议知识,又花了许久功夫 ...
- NET WebAPi之断点续传下载(下)
NET WebAPi之断点续传下载(下) 前言 上一篇我们穿插了C#的内容,本篇我们继续来讲讲webapi中断点续传的其他情况以及利用webclient来实现断点续传,至此关于webapi断点续传下载 ...
- 在ASP.NET中支持断点续传下载大文件(ZT)
IE的自带下载功能中没有断点续传功能,要实现断点续传功能,需要用到HTTP协议中鲜为人知的几个响应头和请求头. 一. 两个必要响应头Accept-Ranges.ETag 客户端每次提交 ...
- 在ASP.NET中支持断点续传下载大文件
IE的自带下载功能中没有断点续传功能,要实现断点续传功能,需要用到HTTP协议中鲜为人知的几个响应头和请求头. 一. 两个必要响应头Accept-Ranges.ETag 客户端每次提交下 ...
- Asp.Net WebAPI及相关技术介绍(含PPT下载)
此PPT讲述了Asp.Net WebAPI及相关Web服务技术发展历史. 共80多页,Asp.Net WebAPI在讲到第36页的时候才会出现,因为这个技术不是凭空产生的,它有着自己的演变进化的历史. ...
- ASP.NET如何实现断点续传的上传、下载功能?
1 背景 用户本地有一份txt或者csv文件,无论是从业务数据库导出.还是其他途径获取,当需要使用蚂蚁的大数据分析工具进行数据加工.挖掘和共创应用的时候,首先要将本地文件上传至ODPS,普通的小文件通 ...
- Asp.Net WebApi核心对象解析(下篇)
在接着写Asp.Net WebApi核心对象解析(下篇)之前,还是一如既往的扯扯淡,元旦刚过,整个人还是处于晕的状态,一大早就来处理系统BUG,简直是坑爹(好在没让我元旦赶过来该BUG),队友挖的坑, ...
随机推荐
- Windows中断那些事儿
搞内核研究的经常对中断这个概念肯定不陌生,经常我们会接触很多与中断相关的术语,按照软件和硬件进行分类: 硬件CPU相关: IRQ.IDT.cli&sti 软件操作系统相关: APC.DPC.I ...
- AFNetWorking设置HTTPRequestHeaders的坑
今天在项目中要封装一个请求头但是用如下方法总是失败: 求其原因不知道: 于是乎改用了属性对象后居然成功了..: // // RequestManager.m // 获取天气demo // // ...
- OSGEarth编译
解决OSGEarth编译时出现error LNK1181: 无法打开输入文件“optimized.lib的问题 以下方案受到了osg大神qq网名为“阿威.小号”的帮助. 在采用Cmake3.6.2创建 ...
- Java中9种IO的读取方式
数据的读写,按照数据类型可以分为两种:字符流和字节流(二者区别?).所以数据读取方式按照数据类型也可以分为两类:字节流的读取和字符流的读取. 一.字节流读取操作: | | |-----1.FileI ...
- photoshop
photoshop裁剪 photoshop裁剪快捷键是C键: 使用photoshop裁剪工具裁切出图片的方法: 选择好所需要的图片,右键保存到本地计算机' 在photoshop中打开这张图片(文件/打 ...
- JSP实现在项目在网页上查询
<table> <caption>user</caption> <tr> <td>编号</td> <td>姓名< ...
- java 引入自定义字体font后出现的硬盘吃光的问题
有个需求要用美术字体在图片上写字 用自定义的文字有两个方法: 1. Font dynamicFont = Font.createFont(Font.TRUETYPE_FONT, InputStream ...
- HDFS 与 GFS 的设计差异
后端分布式系列」前面关于 HDFS 的一些文章介绍了它的整体架构和一些关键部件的设计实现要点. 我们知道 HDFS 最早是根据 GFS(Google File System)的论文概念模型来设计实现的 ...
- .NET Fringe 定义未来
在dotnetconf 2015会宣布了4.12-14 在波特兰召开 .NET Fringe http://dotnetfringe.org/ ,中文社区很少有相关的介绍,本文向大家介绍下这个.NET ...
- C++11 并发指南七(C++11 内存模型一:介绍)
第六章主要介绍了 C++11 中的原子类型及其相关的API,原子类型的大多数 API 都需要程序员提供一个 std::memory_order(可译为内存序,访存顺序) 的枚举类型值作为参数,比如:a ...