Как проверить, загружает ли System.Net.WebClient.DownloadData бинарный файл? - PullRequest
22 голосов
/ 30 сентября 2008

Я пытаюсь использовать WebClient для загрузки файла из Интернета с помощью приложения WinForms. Однако я действительно хочу скачать только HTML-файл. Любой другой тип, который я хочу игнорировать.

Я проверил WebResponse.ContentType, но его значение всегда null.

Кто-нибудь знает, что может быть причиной?

Ответы [ 7 ]

55 голосов
/ 01 октября 2008

Учитывая ваше обновление, вы можете сделать это, изменив .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;
}
3 голосов
/ 04 декабря 2011

Я не уверен, причина, но, возможно, вы еще ничего не загрузили. Это ленивый способ получить тип содержимого удаленного файла / страницы (я не проверял, эффективен ли он в сети. Насколько я знаю, он может загружать огромные порции контента)

        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();
        }
1 голос
/ 30 сентября 2008

WebResponse является абстрактным классом, а свойство ContentType определено в наследующих классах. Например, в объекте HttpWebRequest этот метод перегружен для предоставления заголовка типа содержимого. Я не уверен, какой экземпляр WebResponse использует WebClient. Если вам нужны ТОЛЬКО HTML-файлы, лучше всего использовать объект HttpWebRequest напрямую.

0 голосов
/ 20 июля 2018

Вот метод, использующий TCP, поверх которого построен http. Он вернется при подключении или по истечении времени ожидания (в миллисекундах), поэтому может потребоваться изменить значение в зависимости от вашей ситуации

var result = false;
try {
    using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) {
        var asyncResult = socket.BeginConnect(yourUri.AbsoluteUri, 80, null, null);
        result = asyncResult.AsyncWaitHandle.WaitOne(100, true);
        socket.Close();
    }
}
catch { }
return result;
0 голосов
/ 01 октября 2008

Прошу прощения за то, что не очень ясно. Я написал класс-оболочку, которая расширяет WebClient. В этом классе оболочки я добавил контейнер cookie и выставил свойство тайм-аута для 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;
    }
}
0 голосов
/ 30 сентября 2008

Ваш вопрос немного сбивает с толку: если вы используете экземпляр класса 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, например, открыть файл, прочитать первые 1К символов или около того в строку и посмотреть, содержит ли она подстроку

Также обратите внимание, что это немного расточительно, так как вы всегда будете передавать полный файл, прежде чем решить, хотите ли вы этого или нет. Чтобы обойти это, переключение на классы Net.HttpWebRequest / Response может помочь, но стоит ли дополнительный код, это зависит от вашего приложения ...

0 голосов
/ 30 сентября 2008

Вы могли бы выполнить первый запрос с глаголом HEAD и проверить заголовок ответа типа содержимого? [edit] Похоже, что для этого вам придется использовать HttpWebRequest.

...