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),队友挖的坑, ...
随机推荐
- Android与H5交互
1.初始化WebView控件 webView = (WebView) findViewById(R.id.webview); 2.设置WebView属性 WebSettings webSettings ...
- JNI和NDK编程
Java JNI的本意是Java Native Interface(Java本地接口),它是为了方便Java调用C.C++等本地代码所封装的一层接口.通过Java JNI,用户可以调用C.C++所编写 ...
- php 搜索(查询)功能
今天遇到一个问题:在做“搜索”功能时,输入查询条件后查询不了. 我做的是首页显示数据表package中的内容,但是有个条件,显示在首页的内容还必须是 :字段status=0,且printing=0的数 ...
- php中echo(),print(),print_r(),var_dump()间的区别
echo()函数:输出一个或多个字符串.实际上它并不是一个函数,所以不必对它使用括号,直接用echo就行.然而,如果您希望向echo()传递一个以上的参数,使用括号将会生成解析错误.echo()函数比 ...
- Redis安装配置(Windows版)
近期项目中引入Redis,故记录下来,方便日后查看. 可参考(http://www.cnblogs.com/happyday56/p/3916388.html)不说废话,直奔主题. 一.安装前的准备: ...
- python中的参数问题
python中的有默认参数和可变参数之分 默认参数arg 可变参数args, kargs 默认参数arg就是调用指定参数 可变参数*arg调用时传入的的参数会被python自动包装为列表 可变参数ka ...
- 关于“模仿"和”创新“
互联网刚刚进入中国的前10年,国内互联网企业基本处于模仿和学习阶段.三大门户新浪.搜狐和网易,师从雅虎:现在如日中天的BAT三巨头,百度学习谷歌.阿里巴巴学习亚马逊和EBAY.腾讯学习ICQ. 关于模 ...
- python 之 Django 小案例
一, F Q # F 使用查询条件的值 # # from django.db.models import F # models.Tb1.objects.update(num=F('num')+1) ...
- Android+jsp +html 文件上传案例 已测试 成功通过
我文件上传一直是广大读者一个问题 今天就把成功案例写下 javaweb 网页前段 <%@ page language="java" import="java.uti ...
- 解读ASP.NET 5 & MVC6系列(1):ASP.NET 5简介
ASP.NET 5简介 ASP.NET 5是一个跨时代的改写,所有的功能和模块都进行了独立拆分,做到了彻底解耦.为了这些改写,微软也是蛮 拼的,几乎把.NET Framwrok全部改写了一遍,形成了一 ...