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。
















本文转自xmgdc51CTO博客,原文链接: http://blog.51cto.com/12953214/1941216,如需转载请自行联系原作者





相关文章
|
6月前
|
开发框架 前端开发 .NET
C#编程与Web开发
【4月更文挑战第21天】本文探讨了C#在Web开发中的应用,包括使用ASP.NET框架、MVC模式、Web API和Entity Framework。C#作为.NET框架的主要语言,结合这些工具,能创建动态、高效的Web应用。实际案例涉及企业级应用、电子商务和社交媒体平台。尽管面临竞争和挑战,但C#在Web开发领域的前景将持续拓展。
192 3
|
6月前
|
SQL 开发框架 安全
C#编程与多线程处理
【4月更文挑战第21天】探索C#多线程处理,提升程序性能与响应性。了解C#中的Thread、Task类及Async/Await关键字,掌握线程同步与安全,实践并发计算、网络服务及UI优化。跟随未来发展趋势,利用C#打造高效应用。
198 3
|
14天前
|
C# 开发者
C# 一分钟浅谈:Code Contracts 与契约编程
【10月更文挑战第26天】本文介绍了 C# 中的 Code Contracts,这是一个强大的工具,用于通过契约编程增强代码的健壮性和可维护性。文章从基本概念入手,详细讲解了前置条件、后置条件和对象不变量的使用方法,并通过具体代码示例进行了说明。同时,文章还探讨了常见的问题和易错点,如忘记启用静态检查、过度依赖契约和性能影响,并提供了相应的解决建议。希望读者能通过本文更好地理解和应用 Code Contracts。
29 3
|
1月前
|
安全 C# 数据安全/隐私保护
实现C#编程文件夹加锁保护
【10月更文挑战第16天】本文介绍了两种用 C# 实现文件夹保护的方法:一是通过设置文件系统权限,阻止普通用户访问;二是使用加密技术,对文件夹中的文件进行加密,防止未授权访问。提供了示例代码和使用方法,适用于不同安全需求的场景。
101 0
|
2月前
|
API C#
C# 一分钟浅谈:文件系统编程
在软件开发中,文件系统操作至关重要。本文将带你快速掌握C#中文件系统编程的基础知识,涵盖基本概念、常见问题及解决方法。文章详细介绍了`System.IO`命名空间下的关键类库,并通过示例代码展示了路径处理、异常处理、并发访问等技巧,还提供了异步API和流压缩等高级技巧,帮助你写出更健壮的代码。
42 2
|
2月前
|
安全 程序员 编译器
C#一分钟浅谈:泛型编程基础
在现代软件开发中,泛型编程是一项关键技能,它使开发者能够编写类型安全且可重用的代码。C# 自 2.0 版本起支持泛型编程,本文将从基础概念入手,逐步深入探讨 C# 中的泛型,并通过具体实例帮助理解常见问题及其解决方法。泛型通过类型参数替代具体类型,提高了代码复用性和类型安全性,减少了运行时性能开销。文章详细介绍了如何定义泛型类和方法,并讨论了常见的易错点及解决方案,帮助读者更好地掌握这一技术。
74 11
|
2月前
|
SQL 开发框架 安全
并发集合与任务并行库:C#中的高效编程实践
在现代软件开发中,多核处理器普及使多线程编程成为提升性能的关键。然而,传统同步模型在高并发下易引发死锁等问题。为此,.NET Framework引入了任务并行库(TPL)和并发集合,简化并发编程并增强代码可维护性。并发集合允许多线程安全访问,如`ConcurrentQueue<T>`和`ConcurrentDictionary<TKey, TValue>`,有效避免数据不一致。TPL则通过`Task`类实现异步操作,提高开发效率。正确使用这些工具可显著提升程序性能,但也需注意任务取消和异常处理等常见问题。
48 1
|
2月前
|
安全 数据库连接 API
C#一分钟浅谈:多线程编程入门
在现代软件开发中,多线程编程对于提升程序响应性和执行效率至关重要。本文从基础概念入手,详细探讨了C#中的多线程技术,包括线程创建、管理及常见问题的解决策略,如线程安全、死锁和资源泄露等,并通过具体示例帮助读者理解和应用这些技巧,适合初学者快速掌握C#多线程编程。
78 0
|
3月前
|
存储 C#
揭秘C#.Net编程秘宝:结构体类型Struct,让你的数据结构秒变高效战斗机,编程界的新星就是你!
【8月更文挑战第4天】在C#编程中,结构体(`struct`)是一种整合多种数据类型的复合数据类型。与类不同,结构体是值类型,意味着数据被直接复制而非引用。这使其适合表示小型、固定的数据结构如点坐标。结构体默认私有成员且不可变,除非明确指定。通过`struct`关键字定义,可以包含字段、构造函数及方法。例如,定义一个表示二维点的结构体,并实现计算距离原点的方法。使用时如同普通类型,可通过实例化并调用其成员。设计时推荐保持结构体不可变以避免副作用,并注意装箱拆箱可能导致的性能影响。掌握结构体有助于构建高效的应用程序。
113 7