كيفية التحقق مما إذا كان System.Net.WebClient.DownloadData يقوم بتنزيل ملف ثنائي؟

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

  •  03-07-2019
  •  | 
  •  

سؤال

أحاول استخدامها WebClient لتنزيل ملف من الويب باستخدام تطبيق WinForms.ومع ذلك، أريد حقًا تنزيل ملف HTML فقط.أي نوع آخر سأريد تجاهله.

لقد راجعت WebResponse.ContentType, ولكن قيمته دائما null.

هل لدى أي شخص أي فكرة عما يمكن أن يكون السبب؟

هل كانت مفيدة؟

المحلول

نظرًا للتحديث، يمكنك القيام بذلك عن طريق تغيير .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;
}

نصائح أخرى

لست متأكدًا من السبب، ولكن ربما لم تقم بتنزيل أي شيء بعد.هذه هي الطريقة البطيئة للحصول على نوع محتوى الملف/الصفحة البعيدة (لم أتحقق مما إذا كان هذا فعالاً على السلك.على حد علمي، قد يؤدي إلى تنزيل أجزاء كبيرة من المحتوى)

        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 عبارة عن فئة مجردة ويتم تعريف خاصية ContentType في وراثة الفئات.على سبيل المثال، في كائن HttpWebRequest، يتم تحميل هذه الطريقة بشكل زائد لتوفير رأس نوع المحتوى.لست متأكدًا من مثيل WebResponse الذي يستخدمه WebClient.إذا كنت تريد ملفات HTML فقط، فمن الأفضل استخدام كائن HttpWebRequest مباشرةً.

هل يمكنك إصدار الطلب الأول بفعل HEAD والتحقق من رأس استجابة نوع المحتوى؟[عدل] يبدو أنه سيتعين عليك استخدام HttpWebRequest لهذا الغرض.

سؤالك محير بعض الشيء:إذا كنت تستخدم مثيلًا لفئة 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 الحديثة ستتضمنه دائمًا).في حالة عدم وجود رأس نوع المحتوى، يمكنك الرجوع إلى طريقة أخرى للكشف عن HTML، على سبيل المثال فتح الملف، وقراءة أول ألف حرف أو نحو ذلك في سلسلة، ومعرفة ما إذا كان ذلك يحتوي على السلسلة الفرعية <html>

لاحظ أيضًا أن هذا يعد إهدارًا بعض الشيء، حيث ستقوم دائمًا بنقل الملف الكامل، قبل أن تقرر ما إذا كنت تريده أم لا.للتغلب على ذلك، قد يكون التبديل إلى فئات Net.HttpWebRequest/Response مفيدًا، ولكن ما إذا كانت التعليمات البرمجية الإضافية تستحق ذلك أم لا يعتمد على التطبيق الخاص بك...

أعتذر لعدم الوضوح الشديد.لقد كتبت فئة مجمعة تمتد WebClient.في فئة المجمع هذه، قمت بإضافة حاوية ملفات تعريف الارتباط وعرضت خاصية المهلة لـ 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;
    }
}

فيما يلي طريقة تستخدم 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;
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top