web-dev-qa-db-fra.com

Comment vérifier si System.Net.WebClient.DownloadData télécharge un fichier binaire?

J'essaie d'utiliser WebClient pour télécharger un fichier à partir du Web à l'aide d'une application WinForms. Cependant, je veux vraiment seulement télécharger le fichier HTML. Tout autre type que je voudrai ignorer.

J'ai vérifié le WebResponse.ContentType, mais sa valeur est toujours null.

Quelqu'un a une idée de ce que pourrait être la cause?

22
onescaredycat

En fonction de votre mise à jour, vous pouvez le faire en modifiant le .Method dans 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;
    }
}

Sinon, vous pouvez vérifier l'en-tête lors de la substitution de GetWebRespons (), peut-être en lançant une exception si ce n'est pas ce que vous vouliez:

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

Je ne suis pas sûr de la cause, mais peut-être n'avez-vous encore rien téléchargé. C'est le moyen paresseux d'obtenir le type de contenu d'un fichier/d'une page distante (je n'ai pas vérifié si cela fonctionnait efficacement sur le réseau. Pour autant que je sache, le téléchargement de très gros morceaux de contenu peut être énorme)

        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
RandomInsano

WebResponse est une classe abstraite et la propriété ContentType est définie dans les classes héritées. Par exemple, dans l'objet HttpWebRequest, cette méthode est surchargée pour fournir l'en-tête de type de contenu. Je ne sais pas quelle instance de WebResponse le WebClient utilise. Si vous voulez UNIQUEMENT des fichiers HTML, utilisez au mieux l'objet HttpWebRequest directement.

1
Micah

Voici une méthode utilisant TCP, sur laquelle http est construit. Il reviendra une fois connecté ou après le délai d'attente (millisecondes). Il peut donc être nécessaire de modifier la valeur en fonction de votre situation.

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
Greg

Votre question est un peu déroutante: si vous utilisez une instance de la classe Net.WebClient, Net.WebResponse n’entre pas dans l’équation (à part le fait qu’il s’agit bien d’une classe abstraite et que vous utiliseriez une implémentation concrète telle que HttpWebResponse, comme indiqué dans une autre réponse).

Quoi qu'il en soit, lorsque vous utilisez WebClient, vous pouvez obtenir ce que vous voulez en faisant quelque chose comme ceci:

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

Notez que vous devez vérifier l'existence de l'en-tête Content-Type, car il n'est pas garanti que le serveur le renvoie (bien que la plupart des serveurs HTTP modernes l'incluent toujours). Si aucun en-tête Content-Type n'est présent, vous pouvez utiliser une autre méthode de détection HTML, par exemple en ouvrant le fichier, en lisant les premiers caractères d'environ 1K dans une chaîne et en vérifiant si celle-ci contient la sous-chaîne <html>.

Notez également que cela constitue un peu une perte de temps, car vous transférerez toujours le fichier complet avant de décider si vous le voulez ou non. Pour contourner ce problème, le passage aux classes Net.HttpWebRequest/Response peut être utile, mais la valeur du code supplémentaire dépend de votre application ...

0
mdb

Vous pouvez émettre la première demande avec le verbe HEAD et vérifier l'en-tête de réponse de type de contenu? [edit] Il semble cependant que vous deviez utiliser HttpWebRequest pour cela.

0
Marc Gravell

Je m'excuse de ne pas avoir été très clair. J'ai écrit une classe wrapper qui étend WebClient. Dans cette classe de wrapper, j'ai ajouté un conteneur de cookies et exposé la propriété timeout de WebRequest.

J'utilisais DownloadDataAsync () à partir de cette classe d'encapsuleur et je n'ai pas pu extraire le type de contenu de WebResponse de cette classe d'encapsuleur. Mon intention principale est d’intercepter la réponse et de déterminer si elle est de nature text/html. Si ce n'est pas le cas, j'annulerai cette demande.

J'ai réussi à obtenir le type de contenu après avoir redéfini la méthode WebClient.GetWebResponse (WebRequest, IAsyncResult).

Voici un exemple de ma classe wrapper:

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
onescaredycat