¿Cómo verificar si System.Net.WebClient.DownloadData está descargando un archivo binario?

StackOverflow https://stackoverflow.com/questions/153451

  •  03-07-2019
  •  | 
  •  

Pregunta

Estoy tratando de usar WebClient para descargar un archivo de la web usando una aplicación WinForms. Sin embargo, realmente solo quiero descargar el archivo HTML. Cualquier otro tipo que quiera ignorar.

Verifiqué el WebResponse.ContentType, pero su valor siempre es null.

¿Alguien tiene alguna idea de cuál podría ser la causa?

¿Fue útil?

Solución

Dada su actualización, puede hacer esto cambiando el .Method en 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;
    }
}

Alternativamente, puede verificar el encabezado al anular GetWebRespons (), quizás lanzando una excepción si no es lo que deseaba:

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

Otros consejos

No estoy seguro de la causa, pero quizás aún no haya descargado nada. Esta es la manera perezosa de obtener el tipo de contenido de un archivo / página remoto (no he comprobado si esto es eficiente en el cable. Por lo que sé, puede descargar grandes cantidades de contenido)

        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();
        }

WebResponse es una clase abstracta y la propiedad ContentType se define en las clases heredadas. Por ejemplo, en el objeto HttpWebRequest, este método se sobrecarga para proporcionar el encabezado de tipo de contenido. No estoy seguro de qué instancia de WebResponse está utilizando el WebClient. Si SOLO quiere archivos HTML, lo mejor es usar el objeto HttpWebRequest directamente.

¿Podría emitir la primera solicitud con el verbo HEAD y verificar el encabezado de respuesta de tipo de contenido? [edit] Parece que tendrás que usar HttpWebRequest para esto, sin embargo.

Su pregunta es un poco confusa: si está utilizando una instancia de la clase Net.WebClient, Net.WebResponse no entra en la ecuación (aparte del hecho de que es una clase abstracta, y usted ' estaría utilizando una implementación concreta como HttpWebResponse, como se señaló en otra respuesta).

De todos modos, cuando usa WebClient, puede lograr lo que quiere haciendo algo como esto:

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

Tenga en cuenta que debe verificar la existencia del encabezado Content-Type, ya que no se garantiza que el servidor lo devuelva (aunque la mayoría de los servidores HTTP modernos siempre lo incluirán). Si no hay un encabezado Content-Type, puede recurrir a otro método de detección HTML, por ejemplo, abrir el archivo, leer los primeros 1K caracteres más o menos en una cadena y ver si contiene la subcadena & Lt; html < !> gt;

También tenga en cuenta que esto es un poco derrochador, ya que siempre transferirá el archivo completo, antes de decidir si lo desea o no. Para evitarlo, cambiar a las clases Net.HttpWebRequest / Response podría ayudar, pero si el código adicional vale la pena depende de su aplicación ...

Pido disculpas por no haber sido muy claro. Escribí una clase de contenedor que extiende WebClient. En esta clase de contenedor, agregué un contenedor de cookies y expuse la propiedad de tiempo de espera para WebRequest.

Estaba usando DownloadDataAsync () de esta clase de contenedor y no pude recuperar el tipo de contenido de WebResponse de esta clase de contenedor. Mi intención principal es interceptar la respuesta y determinar si es de naturaleza text / html. Si no es así, cancelaré esta solicitud.

Logré obtener el tipo de contenido después de anular el método WebClient.GetWebResponse (WebRequest, IAsyncResult).

La siguiente es una muestra de mi clase de contenedor:

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;
    }
}

Aquí hay un método que usa TCP, sobre el cual se construye http. Volverá cuando esté conectado o después del tiempo de espera (milisegundos), por lo que es posible que sea necesario cambiar el valor según su situación

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;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top