如何检查System.Net.WebClient.DownloadData是否正在下载二进制文件?

时间:2020-03-06 14:55:39  来源:igfitidea点击:

我正在尝试使用WebClient使用WinForms应用程序从Web下载文件。但是,我真的只想下载HTML文件。我将要忽略的任何其他类型。

我检查了" WebResponse.ContentType",但它的值始终为" null"。

任何人都知道可能是什么原因?

解决方案

我们可以使用HEAD动词发出第一个请求,并检查content-type响应头吗? [edit]看来我们必须为此使用HttpWebRequest。

WebResponse是一个抽象类,并且ContentType属性是在继承类中定义的。例如,在HttpWebRequest对象中,此方法被重载以提供content-type标头。我不确定WebClient正在使用哪个WebResponse实例。如果只需要HTML文件,则最好直接使用HttpWebRequest对象。

问题有点令人困惑:如果我们使用的是Net.WebClient类的实例,则Net.WebResponse不会进入等式中(除了它确实是抽象类,我们将使用另一个响应中指出的具体实现,例如HttpWebResponse)。

无论如何,使用WebClient时,我们可以通过执行以下操作来实现所需的目标:

Dim wc As New Net.WebClient()
Dim LocalFile As String = IO.Path.Combine(Environment.GetEnvironmentVariable("TEMP"), Guid.NewGuid.ToString)
wc.DownloadFile("http://example.com/somefile", LocalFile)
If Not wc.ResponseHeaders("Content-Type") Is Nothing AndAlso wc.ResponseHeaders("Content-Type") <> "text/html" Then
    IO.File.Delete(LocalFile)
Else
    '//Process the file
End If

请注意,我们必须检查Content-Type标头的存在,因为不能保证服务器会返回它(尽管大多数现代HTTP服务器将始终包含它)。如果没有Content-Type标头,则可以使用另一种HTML检测方法,例如打开文件,将前1K个字符读入字符串,然后查看其中是否包含子字符串<html>。

还要注意,这有点浪费,因为在决定是否需要该文件之前,我们总是会传输整个文件。要解决此问题,切换到Net.HttpWebRequest / Response类可能会有所帮助,但是额外的代码是否值得取决于应用程序...

抱歉,我不清楚。我写了一个扩展WebClient的包装器类。在这个包装器类中,我添加了cookie容器并公开了WebRequest的timeout属性。

我正在从此包装器类中使用DownloadDataAsync(),但无法从此包装器类的WebResponse中检索内容类型。我的主要目的是拦截响应并确定其是否为text / html性质。如果不是,我将中止该请求。

覆盖WebClient.GetWebResponse(WebRequest,IAsyncResult)方法后,我设法获得了内容类型。

以下是我的包装器类的示例:

public class MyWebClient : WebClient
{
    private CookieContainer _cookieContainer;
    private string _userAgent;
    private int _timeout;
    private WebReponse _response;

    public MyWebClient()
    {
        this._cookieContainer = new CookieContainer();
        this.SetTimeout(60 * 1000);
    }

    public MyWebClient SetTimeout(int timeout)
    {
        this.Timeout = timeout;
        return this;
    }

    public WebResponse Response
    {
        get { return this._response; }
    }

    protected override WebRequest GetWebRequest(Uri address)
    {
        WebRequest request = base.GetWebRequest(address);

        if (request.GetType() == typeof(HttpWebRequest))
        {
            ((HttpWebRequest)request).CookieContainer = this._cookieContainer;
            ((HttpWebRequest)request).UserAgent = this._userAgent;
            ((HttpWebRequest)request).Timeout = this._timeout;
        }

        this._request = request;
        return request;
    }

    protected override WebResponse GetWebResponse(WebRequest request)
    {
        this._response = base.GetWebResponse(request);
        return this._response;
    }

    protected override WebResponse GetWebResponse(WebRequest request, IAsyncResult result)
    {
        this._response = base.GetWebResponse(request, result);
        return this._response;
    }

    public MyWebClient ServerCertValidation(bool validate)
    {
        if (!validate) ServicePointManager.ServerCertificateValidationCallback += delegate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return true; };
        return this;
    }
}

给定更新,我们可以通过更改GetWebRequest中的.Method来实现:

using System;
using System.Net;
static class Program
{
    static void Main()
    {
        using (MyClient client = new MyClient())
        {
            client.HeadOnly = true;
            string uri = "http://www.google.com";
            byte[] body = client.DownloadData(uri); // note should be 0-length
            string type = client.ResponseHeaders["content-type"];
            client.HeadOnly = false;
            // check 'tis not binary... we'll use text/, but could
            // check for text/html
            if (type.StartsWith(@"text/"))
            {
                string text = client.DownloadString(uri);
                Console.WriteLine(text);
            }
        }
    }

}

class MyClient : WebClient
{
    public bool HeadOnly { get; set; }
    protected override WebRequest GetWebRequest(Uri address)
    {
        WebRequest req = base.GetWebRequest(address);
        if (HeadOnly && req.Method == "GET")
        {
            req.Method = "HEAD";
        }
        return req;
    }
}

或者,我们可以在覆盖GetWebRespons()时检查标头,如果不是我们想要的,可能会引发异常:

protected override WebResponse GetWebResponse(WebRequest request)
{
    WebResponse resp = base.GetWebResponse(request);
    string type = resp.Headers["content-type"];
    // do something with type
    return resp;
}