Search code examples
c#.netresthttpwebrequestwebclient

Webclient + UploadStringAsync + Events: UploadProgressChanged / DownloadProgressChanged


I need to communicate with a REST service.

For some reasons, I have to use a POST request to obtain JSON data from that service. (I think it should use a GET request but this is not under my control and I can't change it...)

The response for one request is around 25-30 MB. Since the response is big I decided to use the WebClient and the UploadStringAsync method.

private string jsonResult = "";

void test()
{
    string credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes(username + ":" + passowrd));  
    using (System.Net.WebClient client = new System.Net.WebClient())
    {
        client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
        client.UploadProgressChanged += new UploadProgressChangedEventHandler(client_UploadProgressHandler);
        client.UploadStringCompleted += new UploadStringCompletedEventHandler(client_UploadStringCompletedHandler);                
        client.Headers.Add("Authorization", "Basic " + credentials);
        client.Encoding = System.Text.Encoding.UTF8;
        Uri uri = new Uri(https://demoapi.org/stuff/prod/0);

        client.UploadStringAsync(uri, "POST", "");
    }
}

private void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
    Console.WriteLine("DOWNLOAD_PROGRESS!");
    Console.WriteLine("Status {0} of {1} bytes. {2} % complete", e.BytesReceived, e.TotalBytesToReceive, e.ProgressPercentage);
}

private static void client_UploadProgressHandler(object sender, UploadProgressChangedEventArgs e)
{
    Console.WriteLine("UPLOAD_PROGRESS!");
    Console.WriteLine("Status {0} of {1} bytes. {2} % complete, {3} of {4} bytes. {5} % complete", e.BytesReceived, e.TotalBytesToReceive, e.ProgressPercentage, e.BytesSent, e.TotalBytesToSend, e.ProgressPercentage);
}

private void client_UploadStringCompletedHandler(Object sender, UploadStringCompletedEventArgs e)
{
    Console.WriteLine("UPLOAD_COMPLETED!");
    jsonResult = e.Result;
}

With the code above, the console shows many times the message UPLOAD_PROGRESS! and once the message UPLOAD_COMPLETED! However, I don't see the message DOWNLOAD_PROGRESS!

UPLOAD_PROGRESS!
UPLOAD_PROGRESS!
....    
UPLOAD_PROGRESS!
UPLOAD_COMPLETED!

Here are my questions:

1) Can I (really) subscribe to the "UploadProgressChanged" Event with the webclient.UploadStringAsync method ?

The Microsoft documentation does not says that the UploadProgressChanged is available for UploadStringAsync but anyhow it seems to be working with the code above.

https://learn.microsoft.com/en-us/dotnet/api/system.net.webclient.uploadprogresschanged?view=netframework-4.7.2

https://learn.microsoft.com/en-us/dotnet/api/system.net.webclient.uploadstringasync?view=netframework-4.7.2

I also looked at the Microsoft referencesource but I don't fully understand it. The delegate 'UploadProgressChanged' does not seem to be present in the UploadStringAsync method.

https://referencesource.microsoft.com/#System/net/System/Net/webclient.cs,d15a560fedc713da,references

So, I'm wondering if it's correct to use "UploadProgressChanged" with the webclient.UploadStringAsync.

2) Can I subscribe to the "DownloadProgressChanged" event with the webclient.UploadStringAsync method ?

3) As my understanding, the webclient is using a WebRequest and WebResponse under the hood. Is it possible to get the progress of both requests ?

I'm asking this because sometimes the body of the request is big and sometimes the reponse is big.

4) If the DownloadProgressChangedEvent is not available, then how can I report the progress of the WebResponse part of the "UploadStringAsync" POST request ?

Thanks


Solution

  • Your code above does an upload so it cannot receive a DownloadProgressChanged event.

    The rule is all a Download or Upload Async methods raise the corresponding Download or Upload progress changed events. Under the scene, UploadStringAsync is using the same code that UploadDataAsync is using (because a string is an array of bytes modulo the character encoding).

    Non-async methods don't raise progress events.

    If you want access to the underlying WebRequest, just create a class that derive from WebClient and override the protected virtual GetWebRequest(Uri address) method, like this:

    public class MyWebClient : WebClient
    {
        protected override WebRequest GetWebRequest(Uri address)
        {
            var request = base.GetWebRequest(address);
            // do something with the request
            // BTW, this is how you can change timeouts or use cookies
            return request;
        }
    }
    

    Note you can also use the more modern HttpClient class (which is fully cross platform, frameworks, etc.) instead of WebClient.