Progress Bar with Httpclient

Progress bar with HttpClient

The best way to go is using Windows.Web.Http.HttpClient instead of System.Net.Http.HttpClient. The first one supports progress.

But if for some reason you want to stick to the System.Net one, you will need to implement your own progress.

Remove the DataWriter, remove the InMemoryRandomAccessStream and add HttpCompletionOption.ResponseHeadersRead to GetAsync call so it returns as soon as headers are received, not when the whole response is received. I.e.:

// Your original code.
HttpClientHandler aHandler = new HttpClientHandler();
aHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
HttpClient aClient = new HttpClient(aHandler);
aClient.DefaultRequestHeaders.ExpectContinue = false;
HttpResponseMessage response = await aClient.GetAsync(
url,
HttpCompletionOption.ResponseHeadersRead); // Important! ResponseHeadersRead.

// To save downloaded image to local storage
var imageFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(
filename,
CreationCollisionOption.ReplaceExisting);
var fs = await imageFile.OpenAsync(FileAccessMode.ReadWrite);

// New code.
Stream stream = await response.Content.ReadAsStreamAsync();
IInputStream inputStream = stream.AsInputStream();
ulong totalBytesRead = 0;
while (true)
{
// Read from the web.
IBuffer buffer = new Windows.Storage.Streams.Buffer(1024);
buffer = await inputStream.ReadAsync(
buffer,
buffer.Capacity,
InputStreamOptions.None);

if (buffer.Length == 0)
{
// There is nothing else to read.
break;
}

// Report progress.
totalBytesRead += buffer.Length;
System.Diagnostics.Debug.WriteLine("Bytes read: {0}", totalBytesRead);

// Write to file.
await fs.WriteAsync(buffer);
}
inputStream.Dispose();
fs.Dispose();

Download file using HttpClient with ProgressBar

Microsoft recommend you don't use HttpWebRequest for new development; use HttpClient instead.

When you use HttpClient you can get progress like this: Progress bar with HttpClient - essentially to read the stream yourself gradually and calculate how far you've read compared to the content length declared in the header. I'd probably use Bruno's answer

How to display upload progress using C# HttpClient PostAsync

Try something like this:

I faced same issue. I fixed it by implementing custom HttpContent. I use this object to track percentage of upload progress, you can add an event to and listen it. You should customize SerializeToStreamAsync method.

internal class ProgressableStreamContent : HttpContent
{
private const int defaultBufferSize = 4096;

private Stream content;
private int bufferSize;
private bool contentConsumed;
private Download downloader;

public ProgressableStreamContent(Stream content, Download downloader) : this(content, defaultBufferSize, downloader) {}

public ProgressableStreamContent(Stream content, int bufferSize, Download downloader)
{
if(content == null)
{
throw new ArgumentNullException("content");
}
if(bufferSize <= 0)
{
throw new ArgumentOutOfRangeException("bufferSize");
}

this.content = content;
this.bufferSize = bufferSize;
this.downloader = downloader;
}

protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
Contract.Assert(stream != null);

PrepareContent();

return Task.Run(() =>
{
var buffer = new Byte[this.bufferSize];
var size = content.Length;
var uploaded = 0;

downloader.ChangeState(DownloadState.PendingUpload);

using(content) while(true)
{
var length = content.Read(buffer, 0, buffer.Length);
if(length <= 0) break;

downloader.Uploaded = uploaded += length;

stream.Write(buffer, 0, length);

downloader.ChangeState(DownloadState.Uploading);
}

downloader.ChangeState(DownloadState.PendingResponse);
});
}

protected override bool TryComputeLength(out long length)
{
length = content.Length;
return true;
}

protected override void Dispose(bool disposing)
{
if(disposing)
{
content.Dispose();
}
base.Dispose(disposing);
}


private void PrepareContent()
{
if(contentConsumed)
{
// If the content needs to be written to a target stream a 2nd time, then the stream must support
// seeking (e.g. a FileStream), otherwise the stream can't be copied a second time to a target
// stream (e.g. a NetworkStream).
if(content.CanSeek)
{
content.Position = 0;
}
else
{
throw new InvalidOperationException("SR.net_http_content_stream_already_read");
}
}

contentConsumed = true;
}
}

Refer :

  • https://github.com/paulcbetts/ModernHttpClient/issues/80
  • Progress bar for HttpClient uploading
  • https://forums.xamarin.com/discussion/18649/best-practice-to-upload-image-selected-to-a-web-api

Angular HttpClient - show spinner/progress indicator while waiting for service to respond - progress events

I've combined @aravind answer and this post (https://alligator.io/angular/httpclient-intro/) to piece together a solution. This leverages Angular's Http client progress events to turn the spinner on/off and also handles errors.

component:

showSpinner = false;
this.shortFormService.postShortForm(formModel)
.subscribe(

(event: HttpEvent<any>) => {
console.log(event)
switch (event.type) {
case HttpEventType.Sent:
this.showSpinner = true;
console.log('Request sent!');
break;
case HttpEventType.ResponseHeader:
console.log('Response header received!');
break;
case HttpEventType.UploadProgress:
const percentDone = Math.round(100 * event.loaded / event.total);
console.log(`File is ${percentDone}% uploaded.`);
case HttpEventType.DownloadProgress:
const kbLoaded = Math.round(event.loaded / 1024);
console.log(`Download in progress! ${ kbLoaded }Kb loaded`);
break;
case HttpEventType.Response:
console.log(' Done!', event.body);
this.showSpinner = false;

}
},
(err: HttpErrorResponse) => {
if (err.error instanceof Error) {
console.log("Client-side error occured.");
} else {
this.router.navigate(['/error', err.error.error]);
console.log("Server-side error occured.");
}
}
)

}

service:

postShortForm(shortForm: any) {
const req = new HttpRequest('POST', this.preCheckUrl, shortForm, {
reportProgress: true,
});
return this.httpNew.request(req)
.retry(3)
}

Progress info using HttpClient

I got the solution of my problem but it is just a little miss in my Second Attempt.

private async Task CheckProgress()
{
var df = httpClient.GetAsync(new Uri(uriString, UriKind.Absolute))

df.Progress = (res, progress) =>
{
// no separate event handler.
}

await df;
}

It is just working fine. Hope it will help someone cheers:)

How to implement progress reporting for Portable HttpClient

I wrote the following code to implement progress reporting. The code supports all the platforms I wanted; however, you need to reference the following NuGet packages:

  • Microsoft.Net.Http
  • Microsoft.Bcl.Async

Here is the code:

public async Task DownloadFileAsync(string url, IProgress<double> progress, CancellationToken token)
{
var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);

if (!response.IsSuccessStatusCode)
{
throw new Exception(string.Format("The request returned with HTTP status code {0}", response.StatusCode));
}

var total = response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value : -1L;
var canReportProgress = total != -1 && progress != null;

using (var stream = await response.Content.ReadAsStreamAsync())
{
var totalRead = 0L;
var buffer = new byte[4096];
var isMoreToRead = true;

do
{
token.ThrowIfCancellationRequested();

var read = await stream.ReadAsync(buffer, 0, buffer.Length, token);

if (read == 0)
{
isMoreToRead = false;
}
else
{
var data = new byte[read];
buffer.ToList().CopyTo(0, data, 0, read);

// TODO: put here the code to write the file to disk

totalRead += read;

if (canReportProgress)
{
progress.Report((totalRead * 1d) / (total * 1d) * 100);
}
}
} while (isMoreToRead);
}
}

The using it is as simple as:

var progress = new Microsoft.Progress<double>();
progress.ProgressChanged += (sender, value) => System.Console.Write("\r%{0:N0}", value);

var cancellationToken = new CancellationTokenSource();

await DownloadFileAsync("http://www.dotpdn.com/files/Paint.NET.3.5.11.Install.zip", progress, cancellationToken.Token);

How to use HttpClient to both report on progress and return data from a slow method?

You can create a loader service.

So in your request you can do like:

getConfig() {

this.loaderService.display(true); <-- Show the loading

return this.http.get<Config>(this.configUrl)
.pipe(
retry(3), // retry a failed request up to 3 times
catchError(
this.loaderService.display(false); <-- Hide the loading in case of error
this.handleError
) // then handle the error
);

this.loaderService.display(false); <-- Hide the loading

}

I used that tutorial -> https://medium.com/@zeljkoradic/loader-bar-on-every-http-request-in-angular-6-60d8572a21a9

Showing how I do to get the data being returned by the api method:

I created a service that is responsible for the communication with the API.

service -> let's call configService

    //First way
getConfigInService() {
const response =
this.http.get<Config>(this.configUrl);
return response;
}

//Second way
getConfigInService() {
return this.http.get<Config>(this.configUrl)
.pipe (
map((config: any) => {
return config.data; --> Have to analyse your json structure that is returned
})
);
}

Then in my component I call that service and treat the returns:

component

  private getConfigInComponent(): void {
this.loaderService.display(true); <-- Show the loading
this.configService.getConfigInService()
.subscribe( (config: any) => {
this.config = config;
this.loaderService.display(false); <-- Hide the loading

}, (error: any) => {
console.error("Erro-> ", error);
this.loaderService.display(false); <-- Hide the loading in case of error
});

Here is a link that may could help -> Angular 6 http.get - displaying returned object in console

Progress bar for HttpClient uploading

Assuming that HttpClient (and underlying network stack) isn't buffering you should be able to do this by overriding HttpContent.SerializeToStreamAsync. You can do something like the following:

        const int chunkSize = 4096;
readonly byte[] bytes;
readonly Action<double> progress;

protected override async Task SerializeToStreamAsync(System.IO.Stream stream, System.Net.TransportContext context)
{
for (int i = 0; i < this.bytes.Length; i += chunkSize)
{
await stream.WriteAsync(this.bytes, i, Math.Min(chunkSize, this.bytes.Length - i));
this.progress(100.0 * i / this.bytes.Length);
}
}

In order to avoid being buffered by HttpClient you either need to provide a content length (eg: implement HttpContent.TryComputeLength, or set the header) or enable HttpRequestHeaders.TransferEncodingChunked. This is necessary because otherwise HttpClient can't determine the content length header, so it reads in the entire content to memory first.

On the phone 8 you also need to disable AllowAutoRedirect because WP8 has a bug in the way it handles redirected posts (workaround is to issue a HEAD first, get the redirected URL, then send the post to the final URL with AllowAutoRedirect = false).



Related Topics



Leave a reply



Submit