Я работаю над написанием своих первых веб-сервисов REST (используя бета-версию MVC 4 и шикарный новый Web Api), и у меня возникла проблема при использовании метода HttpClient.PutAsync
Обзор состоит в том, что я пытаюсь написать процесс загрузки файла, где я делю файл на части и использую метод MVC 4 WebApi HttpClient.PutAsync. Если я просто перебираю свои фрагменты и вызываю PutAsync, то все в целом нормально. Однако иногда серверу не удается записать конкретный фрагмент, и мне нужно сообщить об ошибке клиенту, чтобы можно было повторить загрузку фрагмента. Мне также нужно знать, когда загрузка завершена, поэтому можно выполнить следующий этап на этапе загрузки (например, после загрузки файла необходимо выполнить запись в БД, а также выполнить некоторую дополнительную обработку). быть запущенным на сервере для создания изображений для предварительного просмотра и т.п.).
Проблема в том, что когда я пытаюсь получить доступ к ответу из метода PutAsync, все просто попадает в кричащую кучу - первый кусок отправляется клиентом, но никогда не принимается сервером, и весь процесс загрузки переходит в остановка.
Вот серверный код для обработки метода PutAsset (для ясности я удалил весь нерелевантный код). Кажется, все работает нормально - файлы могут быть успешно загружены и собраны:
<HttpPut()>
Public Function PutArtwork(assetID As String, data As AssetBlock) As HttpResponseMessage(Of AssetBlockResponse)
'Get the info from the tracker file
Dim lContents As String = IO.File.ReadAllText(IO.Path.Combine(My.Settings.ServiceTempDirectory, "Asset/", (data.Tracker & ".tracker")))
Dim lAssetInfo As AssetUpload = JsonConvert.DeserializeObject(Of AssetUpload)(lContents)
If WriteChunkToFile(assetID, lAssetInfo, data) Then
Dim lTracker As AssetBlockResponse = New AssetBlockResponse
lTracker.TrackerGID = data.Tracker
Return New HttpResponseMessage(Of AssetBlockResponse)(lTracker, HttpStatusCode.Accepted)
Else
tracker.TrackerGID = Guid.NewGuid.ToString
tracker.ErrorCode = ApiErrorCode.FileChunkWriteFailed
tracker.Message = String.Format("Write error for chunk {0-{1}", data.Offset, data.Length - 1)
Dim lResponse As HttpResponseMessage(Of AssetBlockResponse) = New HttpResponseMessage(Of AssetBlockResponse)(tracker, HttpStatusCode.InternalServerError)
lResponse.ReasonPhrase = String.Format("Write error for chunk {0-{1}", data.Offset, data.Length - 1)
Return lResponse
End If
End Function
Private Function WriteChunkToFile(assetID As String, info As AssetUpload, data As AssetBlock) As Boolean
Dim lFile As String = GetAssetPath(assetID, String.Empty, data.Tracker & ".upload", 1, False, data.Length)
If IO.File.Exists(lFile) Then
Dim lBytes As Byte() = System.Convert.FromBase64String(data.FileData)
Try
WriteChunk(lFile, lBytes, data.Offset, data.Length)
Return True
Catch ex as Exception
Return False
End Try
End Function
Private Sub WriteChunk(filePath As String, data() As Byte, offset As Integer, length As Integer)
Using lStream = IO.File.Open(filePath, FileMode.Open, FileAccess.Write, FileShare.Write)
lStream.Position = offset
lStream.Write(data, 0, length)
lStream.Close()
End Using
End Sub
И код клиента, вызывающего службу REST:
Dim lClient As HttpClient = New HttpClient
Dim lBlock As AssetBlock = New AssetBlock
lBlock.Tracker = tracker.TrackerGID
lClient.BaseAddress = CreateUploadAddressUri(_BaseAddress, assetInfo)
Dim lLocalChunk As UploadChunkInfo
For Each chunkIterator As UploadChunkInfo In chunks
lLocalChunk = chunkIterator
lBlock.BlockHash = AssetMethods.SHA1HashBytes(lLocalChunk.Chunk)
lBlock.Offset = lLocalChunk.Offset
lBlock.Length = lLocalChunk.Chunk.Length
lBlock.FileData = Convert.ToBase64String(lLocalChunk.Chunk)
Dim lTask = lClient.PutAsync(lClient.BaseAddress, New StringContent(JsonConvert.SerializeObject(lBlock), Text.Encoding.UTF8, "application/json"))
Next
Вышеприведенная версия работает нормально, но это подход «забыть». Я хотел получить ответ от PutAsync, проверить, все ли работает, а затем перейти к следующей попытке или повторить проверку по мере необходимости. Поэтому приведенный выше код .PutAsync должен выглядеть следующим образом:
Dim lTask = lClient.PutAsync(lClient.BaseAddress, New StringContent(JsonConvert.SerializeObject(lBlock), Text.Encoding.UTF8, "application/json"))
If lTask.Result.IsSuccessStatusCode Then '!! This line is never hit !!
lTask.Result.Content.ReadAsStringAsync().ContinueWith(Sub(readtask)
lLocalChunk.Status = UploadChunkInfo.ChunkUploadStatus.Uploaded
End Sub, TaskScheduler.FromCurrentSynchronizationContext())
Else
lLocalChunk.Status = UploadChunkInfo.ChunkUploadStatus.Failed
lLocalChunk.RetryCount += 1
End If
Если порция помечена как сбойная, то мы повторяем попытку как часть цикла. После того, как мы повторили попытку фрагмента X раз, мы рассматриваем весь процесс загрузки как сбой и сообщаем об этом пользователю. Но выполнение PutAsync с проверкой результатов просто останавливает цикл в его дорожках. Он никогда даже не попадает на метод Put сервера. Fiddler показывает отсутствие трафика на сервер,
У меня совершенно нет идей - есть мысли?