Как правильно в C # скачать файл из интернета и записать его на лету? - PullRequest
1 голос
/ 08 января 2010

Редактировать: Это сделано в Compact Framework, у меня нет доступа к WebClient, поэтому это должно быть сделано с помощью HttpWebRequests.

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

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

Это то, что я пока имею для основного метода загрузки:

    private void StartDownload()
    {
        HttpWebRequest webReq = null;
        HttpWebResponse webRes = null;
        Stream fileBytes = null;
        FileStream saveStream = null;

        try
        {
            webReq = (HttpWebRequest)HttpWebRequest.Create(_url);
            webReq.Headers.Add(HttpRequestHeader.Cookie, "somedata");
            webRes = (HttpWebResponse)webReq.GetResponse();

            byte[] buffer = new byte[4096];
            long bytesRead = 0;
            long contentLength = webRes.ContentLength;

            if (File.Exists(_filePath))
            {
                bytesRead = new FileInfo(_filePath).Length;
            }

            fileBytes = webRes.GetResponseStream();
            fileBytes.Seek(bytesRead, SeekOrigin.Begin);

            saveStream = new FileStream(_filePath, FileMode.Append, FileAccess.Write);

            while (bytesRead < contentLength)
            {
                int read = fileBytes.Read(buffer, 0, 4096);
                saveStream.Write(buffer, 0, read);
                bytesRead += read;
            }
            //set download status to complete
            //_parent
        }
        catch
        {
            if (Thread.CurrentThread.ThreadState != ThreadState.AbortRequested)
            {
               //Set status to error.
            }
        }
        finally
        {
            saveStream.Close();
            fileBytes.Close();
            webRes.Close();
            saveStream.Dispose();
            fileBytes.Dispose();
            saveStream = null;
            fileBytes = null;
            webRes = null;
            webReq = null;
        }
    }

Должен ли я загружать больший буфер? Должен ли я записывать буфер в файл так часто (каждые 4 КБ?) Должен ли там быть какой-то спящий поток, чтобы гарантировать, что не весь процессор используется? Я думаю, что сообщать об изменении прогресса каждые 4 КБ - это глупо, поэтому я планировал делать это через каждые 64 КБ.

Нужны общие советы или что-то не так с моим кодом.

Ответы [ 5 ]

2 голосов
/ 08 января 2010

Во-первых, я бы избавился от предложения finally и изменил код для использования предложений " USING ".

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

Например:

using (HttpWebRequest webReq = (HttpWebRequest)HttpWebRequest.Create(_url)) {
    /* more code here... */
}

Во-вторых, я бы не стал создавать экземпляры моих переменных в голове с нулевыми значениями (в стиле Pascal). См. Пример выше.

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

2 голосов
/ 08 января 2010

В полной структуре самый простой способ сделать это - использовать метод WebClient класса DownloadFile , например:

using(var wc = new WebClient()) {
    wc.DownloadFile(url, filePath);
}

РЕДАКТИРОВАТЬ : Чтобы сообщить о ходе загрузки, позвоните DownloadFileAsync и прослушайте событие DownloadProgressChanged .Вы также можете отменить загрузку, вызвав метод CancelAsync .

1 голос
/ 08 января 2010

С точки зрения пользовательского опыта вы сможете ответить на многие из этих вопросов, взглянув на такое приложение, как Internet Explorer или Firefox. Например;

  1. В Internet Explorer новые данные сообщаются каждые несколько килобайт, вплоть до отметки в один мегабайт. После этого он сообщается с шагом 100 килобайт.
  2. Как часто вы пишете в буфер, зависит от того, разрешаете ли вы восстановление после сброса соединения. Если вы похожи на IE и заставляете пользователя начинать с нуля, на самом деле не имеет значения, как часто вы сохраняете буфер, если вы это делаете в конце концов. Установите свои сбережения на основе «приемлемого убытка».
  3. Ваше приложение, очевидно, не должно занимать 100% ЦП, так как это не очень хороший этикет в мире программирования. Пусть ваши потоки хотя бы спят достаточно долго, чтобы не доминировать в процессоре.

Ваш код, вообще говоря, является функциональным, хотя он может выдержать много рефакторинга, чтобы сделать его немного чище / легче для чтения. Возможно, вы также захотите использовать класс WebClient в .NET, но если это учебное упражнение, вы делаете это правильно.

Удачи! Вы на правильном пути.

0 голосов
/ 08 января 2010

Если вам нужно отслеживать прогресс, используйте WebClient.DownloadFileAsync вместе с событиями DownloadProgressChanged и DownloadFileCompleted

WebClient wc = new WebClient();
wc.DownloadProgressChanged += wc_DownloadProgressChanged;
wc.DownloadFileCompleted += wc_DownloadFileCompleted;
wc.DownloadFileAsync(sourceUri, localPath);

...

private void wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
    ...
}

private void wc_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
    ...
}
0 голосов
/ 08 января 2010

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

Например, здесь: http://www.codeproject.com/KB/IP/MyDownloader.aspx

Еще лучше, возьмите существующий код и измените его под свои нужды. (Вот почему код размещается там в первую очередь.)

...