如何检查System.Net.WebClient.DownloadData是否正在下载二进制文件?
我正在尝试使用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; }