2014-01-08 5 views
6

Я использую API Sharefile, который отправляет HTTP-запросы и получает их ответы. Они создаются через URL-адреса и всегда используют одну и ту же функцию. Вот.Отслеживание прогресса HttpWebRequest и HttpWebResponse

Private Function InvokeShareFileOperation(ByVal requestUrl As String) As JObject 

    Dim request As HttpWebRequest = WebRequest.Create(requestUrl) 
    Dim response As HttpWebResponse = request.GetResponse() 

    Dim reader As StreamReader = New StreamReader(response.GetResponseStream()) 

    Dim json As String = reader.ReadToEnd() 
    response.Close() 
    Return JObject.Parse(json) 

End Function 

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

(EDIT) Кстати, это вторая строка кода (ниже), которая занимает больше времени, то есть операцию отслеживания.

Dim response As HttpWebResponse = request.GetResponse() 
+0

Вы должны поставить 'HttpWebResponse и' 'StreamReader' в Using' блоков, особенно с вами используйте их часто. Это может стать частью замедления. –

+0

Что вы думаете о моем ответе? – srka

+0

Какой размер является файлом JSON, возвращаемым API? Если он только мал, например, менее 1k, тогда он будет считываться из потока ответов в одном прочтении. Это сделало бы ход прогресса бессмысленным, поскольку есть только одно измерение, и получение прогресса «GetResponse()» невозможно. В вашем ответе на @srka казалось, что длина содержимого составляет всего 50 байт. – Geezer68

ответ

1

Возможно, простой секундомер - это способ начать?

Dim timer As System.Diagnostics.Stopwatch = New Stopwatch() 

    Dim request As HttpWebRequest = WebRequest.Create(requestUrl) 

    timer.Start() 
    Dim response As HttpWebResponse = request.GetResponse() 
    timer.Stop() 

    Dim reader As StreamReader = New StreamReader(response.GetResponseStream()) 
    Dim json As String = reader.ReadToEnd() 
    response.Close() 

    Label1.Text = "Secs:" & timer.Elapsed.ToString() 
+0

Спасибо @ carleson. Хорошо, ваше предложение хорошее: я действительно понял, что время прошло. Но я не могу связать это с ходом операции. Я не знаю, будет ли это продолжаться 5 или 10 секунд (это переменная), и поэтому я не могу сказать, будет ли я в 4 секунды на 80% или на 40% от операции. – chiapa

2

Я уверен, что вы хотите reader.BaseStream.Length, так что вы можете знать длину, прежде чем читать. (По крайней мере, я сделал, поэтому я попробовал) Но он бросил NotSupportedException с сообщением This stream does not support seek operations. Так что я гугле StreamReader + This stream... и нашел эту ссылку SO:

Error “This stream does not support seek operations” in C#

Так короткий ответ: Это не представляется возможным.

+0

ОК, это невозможно. Есть ли способ обойти это? Чтобы достичь того же результата? – chiapa

+0

@chiapa Я еще не нашел решение. Я искал и искал. Я даже попробовал [WebClient] (http://msdn.microsoft.com/en-us/library/system.net.webclient (v = vs.110) .aspx), но он бросил 'NotSupportedException', он не делает Не нужно искать. Поэтому, если «длина контента» не доступна, я думаю, что это потерянная причина. Прости. –

+0

Спасибо за ваши усилия, это действительно сложно. Я не нашел решения самостоятельно, несмотря на то, что не искал ничего нового с прошлой пятницы. Я попробую еще кое-что, может быть, завтра я снова буду копаться в нем. – chiapa

3

EDIT: Я не думаю, что вы сможете измерить прогресс в любом точном виде здесь, поскольку основная часть операции, похоже, зависит от удаленного сервера, обрабатывающего запрос. GetResponse() обрабатывает настройку DNS, соединение, отправку и ожидание удаленного сервера, и это все из ваших рук. Чтение потока ответов является только измеримым, если возвращается заголовок длины содержимого. Лично я показываю прогресс как 20% изначально, 60% при возврате GetResponse, а затем последние 40% могут отображаться постепенно, если у вас есть длина содержимого перед загрузкой или сделана за один проход, как только вы закончите чтение потока ответов.

В качестве веб-запроса вы можете сначала узнать длину содержимого, а затем прочитать поток ответов, используя буфер вместо ReadToEnd(). Это позволяет рассчитать прогресс и отключить уведомления при загрузке ответа.

Dim request As HttpWebRequest = WebRequest.Create(requestUrl) 

Using response As HttpWebResponse = request.GetResponse() 
    Dim contentLength As Long = response.ContentLength 
    Dim bytesReceived As Long 
    Dim bufferLength as Integer = 8192 
    Dim buffer(bufferLength) As Char 
    Dim sb As New StringBuilder 

    Using reader As StreamReader = New StreamReader(response.GetResponseStream()) 
    Do 
     Dim bufferedCount As Integer = reader.Read(buffer, 0, bufferLength) 
     sb.Append(buffer, 0, bufferedCount) 
     bytesReceived += bufferedCount 
     Console.WriteLine(bytesReceived/contentLength * 100 & "%") 
    Loop While bytesReceived < contentLength 
    End Using 

    Return JObject.Parse(sb.ToString) 
End Using 

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

+0

Спасибо за ваш ответ. Цикл представляет собой бесконечный цикл, bytesReceived всегда chiapa

+0

GetResponse() должен получать только заголовки - фактический контент загружается с использованием потока ответов. Невозможно измерить прогресс удаленного сервера, обрабатывающего запрос, до тех пор, пока не вернется GetResponse. Извлечение response.ContentLength может заставить всю загрузку произойти, хотя - вы можете попробовать response.GetResponseHeader («длина содержимого») вместо – Geezer68

+0

Было бы полезно, если бы вы могли предоставить фактические заголовки ответов с удаленного сервера - возможно, используя Fiddler или сделав запрос через браузер и захватив его с помощью инструментов браузера dev. Если ответ отправлен chunked, то я не думаю, что заголовок длины содержимого установлен. Получение длины содержимого является ключевым моментом здесь :) – Geezer68

3

Сначала мы должны выяснить, что замедляется. Запрос не отправляется до тех пор, пока не вызывается GetResponse(), поэтому обработка сервером может занять некоторое время. Загрузка может занять некоторое время. Если ответ мал (относительно скорости соединения), вы не можете сделать многого (вы можете, если сервер принадлежит вам, но мы сосредоточимся на клиенте), потому что вы не можете добиться прогресса с сервера. Если ответ большой, и вы хотите отслеживать загрузку, вы можете сделать это, только если у вас есть . И чтобы получить только заголовки, сервер должен поддерживать метод запроса HEAD. Так вот код:

Imports System 
Imports System.Net 
Imports System.IO 
Imports System.Text 
Imports System.Threading 
Imports Microsoft.VisualBasic 

Public Class Form1 

    Private Function InvokeShareFileOperation(ByVal requestUrl As String) As JObject 
     HTTPWebRequest_GetResponse.Main(requestUrl) 
     ProgressBar1.Value = 0 
     Dim result As String 
     Do 
      Try 
       ProgressBar1.Value = HTTPWebRequest_GetResponse.progress 
      Catch ex As ArgumentOutOfRangeException 
       ProgressBar1.Style = ProgressBarStyle.Marquee 
      End Try 
      If HTTPWebRequest_GetResponse.done = True Then 
       result = HTTPWebRequest_GetResponse.response 
       ProgressBar1.Style = ProgressBarStyle.Continuous 
       ProgressBar1.Value=100 
       Debug.WriteLine(result) 
       Return JObject.Parse(result) 
       Exit Do 
      End If 
     Loop 
    End Function 

End Class 


Public Class RequestState 
    ' This class stores the State of the request. 
    Private BUFFER_SIZE As Integer = 1024 
    Public requestData As StringBuilder 
    Public BufferRead() As Byte 
    Public request As HttpWebRequest 
    Public response As HttpWebResponse 
    Public streamResponse As Stream 

    Public Sub New() 
     BufferRead = New Byte(BUFFER_SIZE) {} 
     requestData = New StringBuilder("") 
     request = Nothing 
     streamResponse = Nothing 
    End Sub 'New 
End Class 'RequestState 


Class HTTPWebRequest_GetResponse 

    Private BUFFER_SIZE As Integer = 1024 
    Public Shared response As String 
    Public Shared done As Boolean = False 
    Public Shared length As Long = 1 
    Public Shared progress As Integer 
    Public Shared myHttpWebRequest As HttpWebRequest 
    Public Shared myRequestState As New RequestState() 

    Shared Sub Main(url As String) 

     Try 
      Dim headRequest As HttpWebRequest = WebRequest.Create(url) 
      headRequest.Method = "HEAD" 
      Dim headResponse As HttpWebResponse = headRequest.GetResponse 
      length = headResponse.ContentLength 
      Debug.WriteLine(length) 
      headResponse.Close() 
      ' Create a HttpWebrequest object to the desired URL. 
      myHttpWebRequest = WebRequest.Create(url) 

      ' Create an instance of the RequestState and assign the previous myHttpWebRequest 
      ' object to its request field. 

      myRequestState.request = myHttpWebRequest 
      'Dim myResponse As New HTTPWebRequest_GetResponse() 

      ' Start the asynchronous request. 
      Dim result As IAsyncResult = CType(myHttpWebRequest.BeginGetResponse(New AsyncCallback(AddressOf RespCallback), myRequestState), IAsyncResult) 

     Catch e As WebException 
      Debug.WriteLine("Main Exception raised!") 
      Debug.WriteLine("Message: " + e.Message) 
      Debug.WriteLine("Status: " + e.Status) 
     Catch e As Exception 
      Debug.WriteLine("Main Exception raised!") 
      Debug.WriteLine("Source : " + e.Source) 
      Debug.WriteLine("Message : " + e.Message) 
     End Try 
    End Sub 'Main 

    Private Shared Sub RespCallback(asynchronousResult As IAsyncResult) 
     Debug.WriteLine("RespCallBack entered") 
     Try 
      ' State of request is asynchronous. 
      Dim myRequestState As RequestState = CType(asynchronousResult.AsyncState, RequestState) 
      Dim myHttpWebRequest As HttpWebRequest = myRequestState.request 
      myRequestState.response = CType(myHttpWebRequest.EndGetResponse(asynchronousResult), HttpWebResponse) 

      ' Read the response into a Stream object. 
      Dim responseStream As Stream = myRequestState.response.GetResponseStream() 
      myRequestState.streamResponse = responseStream 

      ' Begin the Reading of the contents of the HTML page. 
      Dim asynchronousInputRead As IAsyncResult = responseStream.BeginRead(myRequestState.BufferRead, 0, 1024, New AsyncCallback(AddressOf ReadCallBack), myRequestState) 
      Return 
     Catch e As WebException 
      Debug.WriteLine("RespCallback Exception raised!") 
      Debug.WriteLine("Message: " + e.Message) 
      Debug.WriteLine("Status: " + e.Status) 
     Catch e As Exception 
      Debug.WriteLine("RespCallback Exception raised!") 
      Debug.WriteLine("Source : " + e.Source) 
      Debug.WriteLine("Message : " + e.Message) 
     End Try 
    End Sub 'RespCallback 

    Private Shared Sub ReadCallBack(asyncResult As IAsyncResult) 
     Debug.WriteLine("ReadCallBack entered") 
     Try 

      Dim myRequestState As RequestState = CType(asyncResult.AsyncState, RequestState) 
      Dim responseStream As Stream = myRequestState.streamResponse 
      Dim read As Integer = responseStream.EndRead(asyncResult) 
      ' Read the HTML page. 
      If read > 0 Then 
       myRequestState.requestData.Append(Encoding.ASCII.GetString(myRequestState.BufferRead, 0, read)) 
       If length = -1 Or length = 0 Then 
        progress = -1 
       Else 
        progress = myRequestState.BufferRead.Length * 100/length 
        Debug.WriteLine(progress) 
       End If 
       Dim asynchronousResult As IAsyncResult = responseStream.BeginRead(myRequestState.BufferRead, 0, 1024, New AsyncCallback(AddressOf ReadCallBack), myRequestState) 

      Else 
       If myRequestState.BufferRead.Length > 1 Then 
        Dim fullResponse As String = myRequestState.requestData.ToString 
        response = fullResponse.Substring(0, fullResponse.IndexOf("</body>")).Substring(fullResponse.IndexOf(">", fullResponse.IndexOf("<body")) + 2) 'Returns only body 
        ' Release the HttpWebResponse resource. 
        myRequestState.response.Close() 
        done = True 
        Debug.WriteLine(done) 
       End If 

       responseStream.Close() 
      End If 

     Catch e As WebException 
      Debug.WriteLine("ReadCallBack Exception raised!") 
      Debug.WriteLine("Message: " + e.Message) 
      Debug.WriteLine("Status: " + e.Status) 
     Catch e As Exception 
      Debug.WriteLine("ReadCallBack Exception raised!") 
      Debug.WriteLine("Source : " + e.Source) 
      Debug.WriteLine("Message : " + e.Message) 
     End Try 
    End Sub 'ReadCallBack 
End Class 'HttpWebRequest_BeginGetResponse 

Я взял код из http://msdn.microsoft.com/en-us/library/debx8sh9(v=vs.110).aspx и изменил его.

EDIT: Код теперь возвращает только тело и ответ закрыт.

EDIT2: Как сказал @ Geezer68, это не на 100% точнее, но это нормально для отображения прогресса пользователю.

+0

Я могу протестировать ваш ответ сегодня, спасибо – chiapa

+0

Простите @srka, ваше решение не работает, оно вызывает бесконечный цикл – chiapa

+0

@chiapa Это отлично работает для меня. Где петля infinte? – srka

0

Вот ссылка на Microsoft примера где вы установили размер буфера и вызов обратно на объект ответа https://msdn.microsoft.com/en-us/library/86wf6409%28v=vs.110%29.aspx

+0

«Хотя эта ссылка может ответить на вопрос, лучше включить основные части ответа здесь и укажите ссылку для справки. Ответные ссылки могут стать недействительными, если связанная страница изменится ». – Ghost

Смежные вопросы