在 C# 中,除了 WebClient 我们还可以使用一组 WindowsAPI 来完成下载任务。这就是 Windows Internet,简称 WinINet。本文通过一个 demo 来介绍 WinINet 的基本用法和一些实用技巧。

接口介绍

相比 WebClient 的用法,Win32API 在使用时可能会烦琐一些。所以先把用到的 API 简单介绍一下。

资源的初始化和释放

InternetOpen
这是需要调用的第一个方法,它会初始化内部数据结构,为后面的调用做准备。

InternetCloseHandle
这个方法用来关闭使用中打开的 Internet 句柄,释放资源。

建立到服务器的连接

InternetOpenUrl
这是一个通用的函数,应用程序可以用它来请求数据(只要是 WinINet 支持的协议就可以)。尤其是当我们仅仅想要通过一个 URL 获取数据,而不关心通信协议相关的内容时,这个接口就特别合适。该方法会解析参数中的 URL 字符串,然后建立到服务器的连接,并准备下载由 RUL 标识的数据。

检查响应信息

HttpQueryInfo
检索与 HTTP 请求相关的报头信息。主要是查看请求是否成功。

读取响应内容

InternetReadFile
从 InternetOpenUrl 打开的句柄中读取数据。

下载过程

这里我们只介绍下载过程中的关键环节,完整的过程请参考本文的 demo。

InternetOpenUrl

当请求与服务器建立连接时,我们重点考虑三个问题:请求的 url,是否使用 RELOAD 标识, 客户端是否支持 gzip 压缩。

请求的 url 不用多说,这里直接请求一个 http url。

我们不希望拿到客户端缓存中的数据,所以希望每次请求都能够从服务器重新下载。此时需要为 InternetOpenUrl 方法传入 INTERNET_FLAG_RELOAD 标识。
当前绝大多数的 web 服务器都是支持 gzip 压缩的,我们的客户端当然也要能够解压缩服务器传回来的 gzip 格式的数据。所以我们要在请求中告诉服务器,客户端是能够处理 gzip 数据的。只有这样,服务器才会主动的返回 gzip 格式的数据。
代码如下:

string referer = "Referer: xxxxxx\nAccept-Encoding: gzip";
// INTERNET_FLAG_RELOAD -> 0x80000000
// 跳过缓存,强制从原始的服务器下载数据
hInetFile = NativeMethods.InternetOpenUrl(this._hInet, uri.AbsoluteUri, referer, referer.Length, 0x80000000, IntPtr.Zero);

HttpQueryInfo

接下来我们开始检查前面发送的请求返回的 header 中的信息。主要是:请求的资源是否存在,返回的数据有多长,返回的文件的原始名称是什么,返回的数据是以什么格式被压缩的。

我们先要通过检查返回的状态码来确定请求是否成功,也就是返回的是不是 200。

byte[] content = new byte[BufferSize];
int count = BufferSize;
int temp = ;
NativeMethods.HttpQueryInfo(hInetFile, , content, out count, out temp)
statuscode = Encoding.Unicode.GetString(content, , count);

正确返回时,statuscode 应该是“200”。
不要对 HttpQueryInfo 的第二个参数感到奇怪,为了获得请求的返回状态我们就得传入 19。你可以参考Query Onfo Flags 。
用类似的方法可以得到返回数据的长度,原始的文件名称,返回数据的格式。

InternetReadFile

前面一切顺利的话就可以读取数据了。这个方法本身没什么可说的,但出于简化操作的目的,笔者对 InternetReadFile 进行了简单的封装。创建了一个继承自 Stream 的类 MyInternetReadStream。在重写的 Read 方法中调用 InternetReadFile,并且添加了一个回调方法用来计算下载进度等信息。下面是代码概要,完整代码请参考 demo。

public override int Read(byte[] buffer, int offset, int count)
{
int dwNumberOfBytesToRead = Math.Min(BufferSize, count);
int length = ;
NativeMethods.InternetReadFile(this._hInetFile, this._localBuffer, dwNumberOfBytesToRead, out length)
Array.Copy(this._localBuffer, , buffer, offset, length);
this._bytesReadCallback(length, this._contentLength);
return length;
}

Gzip stream

前面我们提到,服务器可能返回的是经过 gzip 压缩的数据,这就需要我们先检查数据的格式。如果是 gzip 格式的数据就需要把它解压缩。其实这在 C# 中是很简单的,我们只要把刚才创建的 MyInternetReadStream 的实例传给 GZipStream 的构造函数,创建一个新的 GZipStream 实例就可以了。

private Stream GetInternetStream(IntPtr hInetFile)
{
//检查数据是不是gzip格式
string contentEncoding = MyWinInet.GetContentEncoding(hInetFile);
if (contentEncoding.IndexOf("gzip", StringComparison.OrdinalIgnoreCase) != -)
{
return new GZipStream(this.ForGZipReadStream(hInetFile), CompressionMode.Decompress, false);
}

}
private Stream ForGZipReadStream(IntPtr hInetFile)
{
return new MyWinInet.MyInternetReadStream(hInetFile, new MyWinInet.MyInternetReadStream.BytesReadCallback(this.BytesReadCallback));
}

至于计算下载进度,实时的下载速度的实现和 《C# 文件下载 : WebClient》中的实现基本相同,请参考上文,或者直接看本文的 demo。

小结

相比 WebClient,使用 WinINet 接口要烦琐不少。当然也有一定的优势,比如《C# 文件下载 : WebClient》中提到的代理问题,WinINet 的默认设置就能处理好 Credentials。不过在笔者看来,更重要的是我们可以选用不同的方式去处理下载问题。

Demo 下载

C# 文件下载 : WinINet的更多相关文章

  1. Winform文件下载之WinINet

    在C#中,除了webclient我们还可以使用一组WindowsAPI来完成下载任务.这就是Windows Internet,简称 WinINet.本文通过一个demo来介绍WinINet的基本用法和 ...

  2. Android 浏览器 —— 使用 WebView 实现文件下载

    对当前的WebView设置下载监听 mCurrentWebView.setDownloadListener(new DownloadListener() { @Override public void ...

  3. ASP.net MVC 文件下载的几种方法(欢迎讨论)

    在ASP.net MVC 中有几种下载文件的方法 前提:要下载的文件必须是在服务器目录中的,至于不在web项目server目录中的文件下载我不知道,但是还挺想了解的. 第一种:最简单的超链接方法,&l ...

  4. 让IIS7.0.0.0支持 .iso .7z .torrent .apk等文件下载的设置方法

    IIS默认支持哪些MIME类型呢,我们可以这样查看:打开IIS管理器(计算机--管理--服务和应用程序--Internet信息服务(IIS)管理器:或者Win+R,输入inetmgr,Enter),在 ...

  5. Android中使用AsyncTask实现文件下载以及进度更新提示

    Android提供了一个工具类:AsyncTask,它使创建需要与用户界面交互的长时间运行的任务变得更简单.相对Handler来说AsyncTask更轻量级一些,适用于简单的异步处理,不需要借助线程和 ...

  6. 利用Tomcat内置的servlet实现文件下载功能

    起因 最近博客所在的VPS挂了又要重装系统,又要重装各种软件. 以前我也经常更换VPS,每次更换都是各种坑爹事情..比如要下载java.下载tomcat.下载mysql..........以前每次我都 ...

  7. 多个文件下载打包生成zip格式下载

    这个多个文件下载生成zip格式必须先引用一个ICSharpCode.SharpZipLib.dll. 代码如下  //将多个文件打包成压缩文件zip格式下载         protected voi ...

  8. .net一般处理程序(httphandler)实现文件下载功能

    Handler文件代码如下: public class MDMExporterWeb : IHttpHandler { public void ProcessRequest(HttpContext c ...

  9. asp.net 文件下载(txt,rar,pdf,word,excel,ppt)

    aspx 文件下载说起来一点都不难,但是在做的过程中还是遇到了一些小小的问题,就是因为这些小小的问题,导致解决起来实在是太难了,其中一个就是Response.End();导致下载文件出现线程终止的情况 ...

随机推荐

  1. 【翻译】MongoDB指南/聚合——聚合管道

    [原文地址]https://docs.mongodb.com/manual/ 聚合 聚合操作处理数据记录并返回计算后的结果.聚合操作将多个文档分组,并能对已分组的数据执行一系列操作而返回单一结果.Mo ...

  2. 谈一谈NOSQL的应用,Redis/Mongo

    1.心路历程 上年11月份来公司了,和另外一个同事一起,做了公司一个移动项目的微信公众号,然后为了推广微信公众号,策划那边需要我们做一些活动,包括抽奖,投票.最开始是没有用过redis的,公司因为考虑 ...

  3. [原] KVM虚拟机网络闪断分析

    背景 公司云平台的机器时常会发生网络闪断,通常在10s-100s之间. 异常情况 VM出现问题时,表现出来的情况是外部监控系统无法访问,猜测可能是由于系统假死,OVS链路问题等等.但是在出现网络问题的 ...

  4. 在centos7上安装ClamAV杀毒,并杀毒(centos随机英文10字母)成功

    前言 上传文件的时候发现总是失败,查看top发现有个进程一直cpu占用80%以上,而且名称还是随机数.kill之后,一会儿又重新生成了.突然发现居然没有在服务端杀毒的经历.在此处补齐. 安装clama ...

  5. iOS UITableView 与 UITableViewController

    很多应用都会在界面中使用某种列表控件:用户可以选中.删除或重新排列列表中的项目.这些控件其实都是UITableView 对象,可以用来显示一组对象,例如,用户地址薄中的一组人名.项目地址. UITab ...

  6. Div Vertical Menu ver5

    这个小功能,如果是算此次,已经是第5次修改了.可以从这里看到前4次:V1, http://www.cnblogs.com/insus/archive/2011/10/17/2215637.html V ...

  7. 【Star CCM+实例】开发一个简单的计算流程.md

    流程开发在CAE过程中处于非常重要的地位. 主要的作用可能包括: 将一些经过验证的模型隐藏在流程中,提高仿真的可靠性 将流程封装成更友好的界面,降低软件的学习周期 流程开发实际上需要做非常多的工作,尤 ...

  8. 微信小程序之用户数据解密(七)

    [未经作者本人同意,请勿以任何形式转载] 经常看到有点的小伙伴在群里问小程序用户数据解密流程,所以打算写一篇关于小程序用户敏感数据解密教程: 加密过程微信服务器完成,解密过程在小程序和自身服务器完成, ...

  9. .NET Core 1.0.1 升级汇总

    ASP.NET Core BUG fix: ASP.NET Routing Port fix for "Request not matching route with defaults&qu ...

  10. 冗余代码都走开——前端模块打包利器 Rollup.js 入门

    之前翻译过一篇文章,介绍了通过 ES2015 的解构赋值语法引入模块,可以让打包工具(browserify)最终编译出来的代码量最小化. 殊不知在 webpack 1.X 版本是无法利用该特性来避免引 ...