Веб-запрос / загрузка не удалась в самом конце - PullRequest
9 голосов
/ 21 ноября 2010

Я обнаружил, что моя загрузка HTTPWebRequest не удалась в самом конце загрузки ... как показано в это видео @ Screenr

Мой код похож наниже

using (var reqStream = req.GetRequestStream())
{
    BinaryWriter reqWriter = new BinaryWriter(reqStream);
    byte[] buffer = new byte[25600]; // 20KB Buffer
    int read = 0, bytesRead = 0;
    while ((read = memStream.Read(buffer, 0, buffer.Length)) > 0) {
        reqWriter.Write(buffer); // at the very last loop, this line causes the error
        bytesRead += read;
        Debug.WriteLine("Percent Done: " + ((double)bytesRead / memStream.Length * 100) + "% " + DateTime.Now);
    }

Я не знаю, нужно ли вам больше кода, я просто не хочу спамить код здесь.исключение ниже

System.Net.WebException was caught
  Message=The request was aborted: The request was canceled.
  Source=System
  StackTrace:
       at System.Net.ConnectStream.CloseInternal(Boolean internalCall, Boolean aborting)
       at System.Net.ConnectStream.System.Net.ICloseEx.CloseEx(CloseExState closeState)
       at System.Net.ConnectStream.Dispose(Boolean disposing)
       at System.IO.Stream.Close()
       at System.IO.Stream.Dispose()
       at QuickImageUpload.ViewModels.ShellViewModel.UploadImage(String filename, String contentType, Byte[] image) in D:\Projects\QuickImageUpload\QuickImageUpload\ViewModels\ShellViewModel.cs:line 190
  InnerException: System.IO.IOException
       Message=Cannot close stream until all bytes are written.
       Source=System
       StackTrace:
            at System.Net.ConnectStream.CloseInternal(Boolean internalCall, Boolean aborting)
       InnerException: 

Обратите внимание на внутреннее исключение «Невозможно закрыть поток, пока не будут записаны все байты».Но я не закрываю потоки в этом цикле, не так ли?

Ответы [ 2 ]

15 голосов
/ 21 ноября 2010

Ну, вы закрываете поток, помещая его в оператор using - но вы должны закрывать его, так что это вряд ли проблема.

Параточек для начала:

  • Я бы не стал использовать BinaryWriter здесь.Это ничего не покупает.Пишите прямо в поток.
  • Если memStream является MemoryStream, вы можете использовать WriteTo:

    using (var reqStream = req.GetRequestStream())
    {
        memStream.WriteTo(reqStream);
    }
    

    Это означает, что вы не будете получать диагностику, но это делает код проще:)

Теперь, что касается того, что происходит ... моя догадка в том, что вы получаете исключение ввызов Write, но это исключение эффективно заменяется на исключение, создаваемое при закрытии потока.

У меня есть идея, почему исключение также может быть выдано ...

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

reqWriter.Write(buffer);

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

reqWriter.Write(buffer, 0, read);

... но лично я бы не стал.Я бы или просто использовал бы MemoryStream.WriteTo или , чтобы написать прямо в поток запросов ... как я уже говорил, BinaryWriter на самом деле ничего не покупает.

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

  • Длина содержимого установлена ​​на 30000
  • Получить поток и ввести с помощью оператора
  • Считать 25600 байт из потока памяти
  • Записать 25600 байт: поток подтверждает правильность
  • Считать 4400 байт из потока памяти (т. Е. Все оставшиеся данные)
  • Попробуйте немедленно записать 25600 байт: поток решает, что это неверно, и выдает IOException
  • Неявный блок finally оператора using, затем удаляет поток, который закрывает его
  • Поток замечает, что только25600 байт были успешно записаны, но этого недостаточно, поэтому выбрасывает исключение

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

4 голосов
/ 21 ноября 2010

Вы не можете записать «буфер», так как к концу «буфер» не содержит данных в 20 тыс.

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

reqWriter.Write(buffer, 0, read);
...