Как проверить, загружает ли System.Net.WebClient.DownloadData двоичный файл?
Я пытаюсь использовать WebClient
для загрузки файла из Интернета с помощью приложения WinForms. Тем не менее, я действительно хочу только загрузить HTML файл. Любой другой тип, который я хочу игнорировать.
Я проверил WebResponse.ContentType
, но его значение всегда null
.
Кто-нибудь может понять, что может быть причиной?
Ответы
Ответ 1
Учитывая ваше обновление, вы можете сделать это, изменив метод .Method в GetWebRequest:
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;
}
Ответ 2
Я не уверен в этом, но, возможно, вы еще ничего не загрузили. Это ленивый способ получить тип содержимого удаленного файла/страницы (я не проверял, действительно ли это работает на проводе. Насколько мне известно, он может загружать огромные куски контента)
Stream connection = new MemoryStream(""); // Just a placeholder
WebClient wc = new WebClient();
string contentType;
try
{
connection = wc.OpenRead(current.Url);
contentType = wc.ResponseHeaders["content-type"];
}
catch (Exception)
{
// 404 or what have you
}
finally
{
connection.Close();
}
Ответ 3
WebResponse - абстрактный класс, а свойство ContentType определяется в наследующих классах. Например, в объекте HttpWebRequest этот метод перегружен, чтобы обеспечить заголовок типа содержимого. Я не уверен, какой экземпляр WebResponse используется WebClient. Если вы хотите только файлы HTML, лучше всего использовать объект HttpWebRequest.
Ответ 4
Вы можете отправить первый запрос с помощью HEAD-глагола и проверить заголовок ответа типа содержимого? [править] Похоже, вам придется использовать HttpWebRequest для этого.
Ответ 5
Ваш вопрос немного запутан: если вы используете экземпляр класса Net.WebClient, Net.WebResponse не входит в уравнение (кроме того, что он действительно абстрактный класс, а вы - d использовать конкретную реализацию, такую как 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 может помочь, но зависит ли дополнительный код от вашего приложения...
Ответ 6
Извиняюсь, что не очень ясен. Я написал класс оболочки, который расширяет WebClient. В этом классе оболочки я добавил контейнер cookie и выставил свойство timeout для WebRequest.
Я использовал DownloadDataAsync() из этого класса-оболочки, и мне не удалось получить контент-тип из WebResponse этого класса-оболочки. Моя основная цель - перехватить ответ и определить, имеет ли он текст/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;
}
}