如何检查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;
}

