WinINet for Winform file download

In C#, in addition to webclient, we can also use a set of Windows API to complete the download task. This is Windows Internet, or WinINet for short. This article introduces the basic usage and some practical skills of WinINet through a demo. 

  • series of articles

Winform file download WebClient

 

  • Interface introduction

Compared with the usage of WebClient, Win32API may be cumbersome to use. So let's briefly introduce the API used.

 

Initialization and release of resources

InternetOpen

This is the first method that needs to be called, and it initializes the internal data structures in preparation for subsequent calls.

InternetCloseHandle

This method is used to close open Internet handles in use and free resources.

 

establish a connection to the server

InternetOpenUrl

This is a generic function that an application can use to request data (as long as it is a protocol supported by WinINet). Especially when we just want to get data through a URL, and don't care about the content of the communication protocol, this interface is especially suitable. The method parses the URL string in the parameters, then establishes a connection to the server and prepares to download the data identified by the RUL.

 

Check response information

HttpQueryInfo

Retrieve header information related to an HTTP request. Mainly to see if the request is successful.

 

read response content

InternetReadFile

Read data from the handle opened by InternetOpenUrl.

 

  • Download process

Here we only introduce the key links in the download process. For the complete process, please refer to the demo in this article.

 

InternetOpenUrl

When requesting to establish a connection with the server, we focus on three issues: the requested url, whether to use the RELOAD logo, and whether the client supports gzip compression.

 

Needless to say about the requested url, here is a direct request for an http url.

 

We don't want to get the data in the client's cache, so we want to be able to re-download from the server on every request. At this point, you need to pass in the INTERNET_FLAG_RELOAD flag for the InternetOpenUrl method.

 

Most of the current web servers support gzip compression. Of course, our client must be able to decompress the data in gzip format returned by the server. So we have to tell the server in the request that the client can handle gzip data. Only in this way, the server will actively return the data in gzip format.

code show as below:

string referer = "Referer: xxxxxx\nAccept-Encoding: gzip";

// INTERNET_FLAG_RELOAD -> 0x80000000

// Skip the cache and force the data to be downloaded from the original server

hInetFile = NativeMethods.InternetOpenUrl(this._hInet, uri.AbsoluteUri, referer, referer.Length, 0x80000000, IntPtr.Zero);

  

 

HttpQueryInfo

Next, we start to check the information in the header returned by the request sent earlier. Mainly: whether the requested resource exists, how long is the returned data, what is the original name of the returned file, and what format is the returned data compressed in.

 

We first need to check whether the request is successful by checking the returned status code, that is, whether it returns 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);

  

When returned correctly, the statuscode should be "200".

Don't be surprised by the second parameter of HttpQueryInfo, in order to get the return status of the request we have to pass in 19. You can refer to 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));

}

  

 

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

 

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

 

  • Demo 下载:

WinInetDemo

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326177305&siteId=291194637