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

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

  •  03-07-2019
  •  | 
  •  

Question

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

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

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

Était-ce utile?

La solution

Etant donné 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;
    }
}

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

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

Autres conseils

Je ne suis pas sûr de la cause, mais vous n'avez peut-être 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é son efficacité sur le réseau. Pour autant que je sache, il risque de télécharger d’énormes quantités de contenu)

        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 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 de préférence directement l'objet HttpWebRequest.

Vous pouvez émettre la première requête 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.

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 (mis à part le fait qu'il s'agit bien d'une classe abstraite, et vous ' d utiliser 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 recourir à une autre méthode de détection HTML, par exemple en ouvrant le fichier, en lisant les premiers caractères environ dans une chaîne et en vérifiant si celle-ci contient la sous-chaîne & Lt; html < !> gt;

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 ...

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

J'utilisais DownloadDataAsync () à partir de cette classe de wrapper et je n'ai pas pu extraire le type de contenu de WebResponse de cette classe de wrapper. 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 d'encapsuleur:

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

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;
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top