Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HttpClient - Size of downloading file before download

I'm implementing download of files with progress bar. I'm using IAsyncOperationWithProgress for this issue, concretely this code. It is working nice, but I'm receiving only count of bytes that were received/downloaded. But I need calculate percentual part for showing progress. That means I need to know total count of bytes at the start of downloading and I didn't find way how to do this effectively.

Following code resolves progress reporting. I tried to get stream length with responseStream.Length but error "This stream does not support seek operations." was thrown.

static async Task<byte[]> GetByteArratTaskProvider(Task<HttpResponseMessage> httpOperation, CancellationToken token, IProgress<int> progressCallback)
        {
            int offset = 0;
            int streamLength = 0;
            var result = new List<byte>();

            var responseBuffer = new byte[500];

            // Execute the http request and get the initial response
            // NOTE: We might receive a network error here
            var httpInitialResponse = await httpOperation;

            using (var responseStream = await httpInitialResponse.Content.ReadAsStreamAsync())
            {
                int read;

                do
                {
                    if (token.IsCancellationRequested)
                    {
                        token.ThrowIfCancellationRequested();
                    }

                    read = await responseStream.ReadAsync(responseBuffer, 0, responseBuffer.Length);

                    result.AddRange(responseBuffer);

                    offset += read;
                    // here I want to send percents of downloaded data
                    // offset / (totalSize / 100)
                    progressCallback.Report(offset);

                } while (read != 0);
            }

            return result.ToArray();
        }

Do you have any idea how to deal with this issue? Or do you have some another way how to download files with progress reporting through HttpClient? I tried to use BackgroundDownloader as well but it was not sufficient for me. Thank you.

like image 706
Jozef Cechovsky Avatar asked Jun 22 '14 08:06

Jozef Cechovsky


People also ask

How to download a file using httpclient?

Here’s the simplest possible example of downloading a file using HttpClient. var httpClient = new HttpClient (); using ( var stream = await httpClient.

How does webclient work with files?

Since the very first versions of the .NET Framework, the WebClient class has provided some really useful methods for working with files. This includes methods to download files, strings, and arbitrary binary data using byte arrays. The WebClient class also includes methods for uploading resources to web servers.

How does httpservletrequest handle uploads?

No matter which way the client uploads, the server handles it the same way. After obtaining the parameters through HttpServletRequest, the resulting Item is classified into ordinary forms and File forms. Through ServletFileUpload, you can set the size and encoding format of the uploaded file.

What is the difference between httpclient and HTTP method?

Although the client side handles it differently, the server side is the same. The most basic function of HttpClient is to implement the Http method. The execution of an Http method involves the interaction of one or more Http requests /Http responses, which is normally handled automatically by HttpClient and transparent to the user.


1 Answers

You can look at the value of the Content-Length header returned by the server, which is stored in your case in httpInitialResponse.Content.Headers. You'll have to find the header in the collection with the corresponding key (i.e. "Content-Length")

You can do it for example like this:

int length = int.Parse(httpInitialResponse.Content.Headers.First(h => h.Key.Equals("Content-Length")).Value.First());

(You have to make sure first that a Content-Length header has been sent by the server, otherwise the line above will fail with an exception)

Your code would look like:

static async Task<byte[]> GetByteArrayTaskProvider(Task<HttpResponseMessage> httpOperation, CancellationToken token, IProgress<int> progressCallback)
{
    int offset = 0;
    int streamLength = 0;
    var result = new List<byte>();

    var responseBuffer = new byte[500];

    // Execute the http request and get the initial response
    // NOTE: We might receive a network error here
    var httpInitialResponse = await httpOperation;
    var totalValueAsString = httpInitialResponse.Content.Headers.SingleOrDefault(h => h.Key.Equals("Content-Length"))?.Value?.First());
    int? totalValue = totalValueAsString != null ? int.Parse(totalValueAsString) : null;

    using (var responseStream = await httpInitialResponse.Content.ReadAsStreamAsync())
    {
       int read;
       do
       {
           if (token.IsCancellationRequested)
           {
              token.ThrowIfCancellationRequested();
           }

           read = await responseStream.ReadAsync(responseBuffer, 0, responseBuffer.Length);
           result.AddRange(responseBuffer);

           offset += read;
           if (totalSize.HasValue)
           {
              progressCallback.Report(offset * 100 / totalSize);
           }
           //for else you could send back the offset, but the code would become to complex in this method and outside of it. The logic for "supports progress reporting" should be somewhere else, to keep methods simple and non-multi-purpose (I would create a method for with bytes progress and another for percentage progress)
       } while (read != 0);
    }
    return result.ToArray();
}
like image 110
Thierry Avatar answered Oct 10 '22 02:10

Thierry