Дождитесь загрузки страницы перед загрузкой с помощью WebClient

У меня есть несколько URL-адресов, хранящихся в текстовом файле, каждый из которых является ссылкой, ведущей к смайлику Facebook, например https://www.facebook.com/images/emoji.php/v5/u75/1/16/1f618.png

Я пытаюсь загрузить эти изображения и сохранить их на своем диске. Я использую WebClient с DownloadFileAsync, что-то вроде

using (var client = new WebClient())  
{
    client.DownloadFileAsync(imgURL, imgName);
}

Моя проблема заключается в том, что даже если количество URL-адресов невелико, скажем, 10, некоторые изображения загружаются нормально, некоторые выдают ошибку повреждения файла. Поэтому я подумал, что мне нужно дождаться загрузки файлов до конца, и добавил событие DownloadFileCompleted, подобное этому

using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Linq;
using System.Net;

class Program
{
    static Queue<string> q;

    static void Main(string[] args)
    {
        q = new Queue<string>(new[] {
            "https://www.facebook.com/images/emoji.php/v5/u51/1/16/1f603.png",
            "https://www.facebook.com/images/emoji.php/v5/ud2/1/16/1f604.png",
            "https://www.facebook.com/images/emoji.php/v5/ud4/1/16/1f606.png",
            "https://www.facebook.com/images/emoji.php/v5/u57/1/16/1f609.png",
            "https://www.facebook.com/images/emoji.php/v5/u7f/1/16/1f60a.png",
            "https://www.facebook.com/images/emoji.php/v5/ufb/1/16/263a.png",
            "https://www.facebook.com/images/emoji.php/v5/u81/1/16/1f60c.png",
            "https://www.facebook.com/images/emoji.php/v5/u2/1/16/1f60d.png",
            "https://www.facebook.com/images/emoji.php/v5/u75/1/16/1f618.png",
            "https://www.facebook.com/images/emoji.php/v5/u1e/1/16/1f61a.png"
        });
        DownloadItem();
        Console.WriteLine("Hit return after 'finished' has appeared...");
        Console.ReadLine();
    }

    private static void DownloadItem()
    {        
        if (q.Any())
        {
            var uri = new Uri(q.Dequeue());
            var file = uri.Segments.Last();

            var webClient = new WebClient();
            webClient.DownloadFileCompleted += DownloadFileCompleted;
            webClient.DownloadFileAsync(uri, file);
        }
        else 
        {
            Console.WriteLine("finished");
        }
    }

    private static void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
    {
        DownloadItem();
    }
}

Это не помогло, и я решил посмотреть внимательнее на файлы, которые повреждены.

Оказалось, что поврежденные файлы на самом деле были не файлами изображений, а HTML-страницами, которые либо имели некоторый код JavaScript для перенаправления на изображение, либо были полными HTML-страницами, в которых говорилось, что мой браузер не поддерживается.

Итак, мой вопрос: как мне на самом деле дождаться, когда файл изображения будет полностью загружен и готов к загрузке?

EDIT Я также пытался удалить оператор using, но это тоже не помогло.


person tube-builder    schedule 16.12.2016    source источник
comment
Возможно, это не связано с вашей текущей проблемой, но вы запускаете асинхронный процесс, а затем удаляете объект, с которым вы начали этот асинхронный процесс (через оператор using). Я хотел бы улучшить ваше управление объектами здесь, чтобы вы не Dispose из ваших WebClient, пока вы на самом деле не закончите. Пока вы все еще пытаетесь понять основы, я, вероятно, предлагаю пока игнорировать асинхронность.   -  person Damien_The_Unbeliever    schedule 16.12.2016
comment
@Damien_The_Unbeliever Пробовал и без асинхронности, но загружает только один файл (возможно, здесь что-то упущено). Об удалении оператора using - это не помогло, см. Мой комментарий ниже.   -  person tube-builder    schedule 16.12.2016
comment
Простое добавление этого редактирования не так полезно, как предоставление минимально воспроизводимого примера, который я просил ранее.   -  person Jon Skeet    schedule 16.12.2016
comment
Ну, это все еще не минимально воспроизводимый пример, не так ли? Это не то, что мы можем скопировать, вставить, скомпилировать, запустить и увидеть проблему. Это то, что я ищу.   -  person Jon Skeet    schedule 16.12.2016
comment
@Jon Skeet, я снова обновил код. Теперь все, что нужно сделать, это создать консольное приложение и добавить в него общедоступный класс. Код из Main вызывает метод Class1 Call.   -  person tube-builder    schedule 16.12.2016
comment
Непонятно, почему вы не включили объявление класса и директивы using и не поместили все это в одно... зачем усложнять людям задачу помочь вам? Я пытаюсь сделать это сейчас - вы рады, что я потом отредактирую ваш вопрос?   -  person Jon Skeet    schedule 16.12.2016
comment
@Jon Skeet Конечно, ты можешь это сделать. Извините, это моя первая попытка предоставить минимальный полный пример кода.   -  person tube-builder    schedule 16.12.2016


Ответы (1)


Ваша загрузка ничего не искажает — просто Facebook решает (иногда это странно), что не хочет предоставлять изображение вашему клиенту.

Похоже, что проблема заключается в отсутствии пользовательского агента. Все, что вам нужно сделать, это указать пользовательский агент, и похоже, что он исправляет это:

webClient.Headers.Add(HttpRequestHeader.UserAgent,
    "Mozilla/5.0 (compatible; http://example.org/)");
person Jon Skeet    schedule 16.12.2016
comment
Ничего себе, я тестирую это сейчас на большей сумме, и это работает. Большое спасибо за помощь! Сам бы я никогда не догадался об этом. - person tube-builder; 16.12.2016
comment
@tube-builder это ответ от человека, который является своего рода легендой. Это обязательно сработает. :) meta.stackexchange.com/questions/9134/jon-skeet-facts - person Atul; 16.12.2016
comment
@Atul Да, я знаю об этом :) - person tube-builder; 16.12.2016