C# 文件下载 : WinINet

简介:

在 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 = 0;
NativeMethods.HttpQueryInfo(hInetFile, 19, content, out count, out temp)
statuscode = Encoding.Unicode.GetString(content, 0, 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 = 0;
    NativeMethods.InternetReadFile(this._hInetFile, this._localBuffer, dwNumberOfBytesToRead, out length)
    Array.Copy(this._localBuffer, 0, 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) != -1)
    {
        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 下载


本文转自sparkdev博客园博客,原文链接:http://www.cnblogs.com/sparkdev/p/6055641.html,如需转载请自行联系原作者

相关文章
|
1月前
|
前端开发 PHP
33 多文件上传及文件下载
路老师分享PHP语言知识,涵盖多文件上传和文件下载功能。多文件上传只需将表单中的文件域名称改为数组形式,文件下载则通过`header()`函数实现强制下载。详细代码示例和操作步骤,助你轻松掌握PHP核心技术。
35 1
|
编解码 Java 应用服务中间件
文件的上传和下载
文件的上传和下载
97 0
|
前端开发 Java Apache
文件上传与下载
文件上传与下载 文件上传也称为upload,是指将本地图片、视频、音频等文件上传到服务器上,可以供其他用户浏览或下载的过程。文件上传在项目中应用非常广泛,我们经常发微博、发微信朋友圈都用到了文件上传功能。 文件上传时,对页面的form表单有如下要求: method=“post” 采用post方式提交数据 enctype=“multipart/form-data” 采用multipart格式上传文件 type=“file” 使用input的file控件上传
|
缓存 Java
sevlet实现下载文件功能
希望做一个小板块,实现文件的上传和下载,那么上传实现了,就需要实现下载,阅读了各位的博客总结了一下。在网页中通过超链接是可以访问我的资源的,浏览器不可访问的资源他就会下载到本地,像一些浏览器可以直接访问的如图片,txt文件浏览器会直接打开。这就需要我们在sevlet中统一处理文件下载。
149 0
sevlet实现下载文件功能
文件下载
文件下载
83 0
下载文件
下载文件
112 0
|
前端开发
文件下载的几种方式
文件下载的几种方式
|
Linux C# iOS开发
使用 C# 下载文件的十八般武艺
文件下载是一个软件开发中的常见需求。本文从最简单的下载方式开始步步递进,讲述了文件下载过程中的常见问题并给出了解决方案。并展示了如何使用多线程提升 HTTP 的下载速度以及调用 aria2 实现非 HTTP 协议的文件下载。
567 0
|
分布式计算 Hadoop 开发者
文件下载| 学习笔记
快速学习文件下载
217 0