Как загрузить файл в строку с обратным вызовом прогресса?

Я хотел бы использовать WebClient (или существует другой более оптимальный вариант?), но существует проблема. Я понимаю, что открытие против течения занимает время, и этого нельзя избежать. Однако чтение его занимает странно намного больше количества времени, сравненного для чтения его полностью сразу.

Существует ли лучший способ сделать это? Я имею в виду два пути, чтобы представить в виде строки и зарегистрировать. Progress мой собственный делегат, и это работает хорошее.


ПЯТОЕ ОБНОВЛЕНИЕ:

Наконец, мне удалось сделать это. Тем временем я проверил некоторые решения, что заставило меня понять, что проблема заключается в другом месте.

Я протестировал пользовательский WebResponse и WebRequest объекты, библиотека libCURL.NET и даже Sockets.

Разницей во времени было gzip сжатие. Сжатая потоковая длина была просто половиной нормальной потоковой длины, и таким образом загрузите время, были меньше чем 3 секунды с браузером.

Я поместил некоторый код, если кто-то захочет знать, как я решил это: (некоторые заголовки не нужны),

public static string DownloadString(string URL)
    {
        WebClient client = new WebClient();
        client.Headers["User-Agent"] = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.1.249.1045 Safari/532.5";
        client.Headers["Accept"] = "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
        client.Headers["Accept-Encoding"] = "gzip,deflate,sdch";
        client.Headers["Accept-Charset"] = "ISO-8859-2,utf-8;q=0.7,*;q=0.3";

        Stream inputStream = client.OpenRead(new Uri(URL));
        MemoryStream memoryStream = new MemoryStream();
        const int size = 32 * 4096;
        byte[] buffer = new byte[size];

        if (client.ResponseHeaders["Content-Encoding"] == "gzip")
        {
            inputStream = new GZipStream(inputStream, CompressionMode.Decompress);
        }

        int count = 0;
        do
        {
            count = inputStream.Read(buffer, 0, size);
            if (count > 0)
            {
                memoryStream.Write(buffer, 0, count);
            }
        }
        while (count > 0); 

        string result = Encoding.Default.GetString(memoryStream.ToArray());
        memoryStream.Close();
        inputStream.Close();
        return result;
    }

Я думаю, что функции asyncro будут почти тем же. Но я буду просто использовать другой поток для увольнения этой функции. Мне не нужен точный признак прогресса.

6
задан Kaminari 27 April 2010 в 00:56
поделиться

2 ответа

Вы получаете только последние iSize байтов из вашего файла, поскольку вы перезаписываете свой буфер на каждой итерации, вы не сохраняете буфер в любом месте. Вот пример того, как сохранить файл в памяти с помощью MemoryStream.

var totalBytes = new MemoryStream(1024 * 1024);
while ((iByteSize = streamRemote.Read(byteBuffer, 0, iByteSize)) > 0)
{
    totalBytes.Write(byteBuffer, 0, iByteSize);
    iRunningByteTotal += iByteSize;

    //Some progress calculation
    if (Progress != null) Progress(iProgressPercentage);
}

Когда вся загрузка будет завершена, вы можете преобразовать ее в текст.

var byteArray = totalBytes.GetBuffer();
var numberOfBytes = totalBytes.Length;
var text = Encoding.Default.GetString(byteArray, 0, numberOfBytes);

Обновление: метод DownloadStringAsync в основном делает то же самое, что и выше, но не дает никаких индикаторов хода выполнения. Однако есть и другие асинхронные методы, которые вызывают событие DownloadProgressChanged .

Обновление 2: По поводу времени ответа. Вы рассчитали время загрузки ресурса с помощью другого инструмента? Основные браузеры имеют встроенную поддержку синхронизации таких сигналов.

Кроме того, это статический файл, который вы обслуживаете, или контент создается на стороне сервера?

Третье, что приходит на ум, - это буферизация на стороне сервера. Например. если свойство Response.Buffer в ASP.Net используется, ничего не будет отправлено клиенту, пока весь файл / страница не будет завершена на стороне сервера. Таким образом, клиенту придется подождать, прежде чем он сможет начать загрузку.

1
ответ дан 17 December 2019 в 20:30
поделиться

Меня очень смущает двойное чтение, но выглядит так, будто вы действительно собираетесь сделать что-то вроде:

        StringBuilder sb = new StringBuilder();           
        using (StreamReader reader = new StreamReader(streamRemote))
        {
            char[] charBuffer = new char[bufferSize];
            int charsRead;
            while ((charsRead = reader.Read(charBuffer, 0, bufferSize)) > 0)
            {
                sb.Append(charBuffer, 0, charsRead);
                //Some progress calculation

                if (Progress != null) Progress(iProgressPercentage);
            }
        }
        string result = sb.ToString();

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

            //[snip]
            int iteration = 0, charsRead;
            while ((charsRead = reader.Read(charBuffer, 0, bufferSize)) > 0)
            {
                sb.Append(charBuffer, 0, charsRead);
                //Some progress calculation
                if((++iteration % 20) == 0 && Progress != null) {
                    Progress(iProgressPercentage);
                }
            }
            //[snip]

Также попробуйте увеличить размер буфера.

1
ответ дан 17 December 2019 в 20:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: