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
How Do Arrays in C# Partially Implement Ilist<T>
Formatting a Float to 2 Decimal Places
Calling a Method Every X Minutes
Find Size of Object Instance in Bytes in C#
Does Disposing Streamreader Close the Stream
Can a Byte[] Array Be Written to a File in C#
Most Efficient Way to Test Equality of Lambda Expressions
Does the Use of Async/Await Create a New Thread
Method Cannot Be Translated into a Store Expression
How to Pass Values (Parameters) Between Xaml Pages
Finding the Default Application for Opening a Particular File Type on Windows
Getting the Http Referrer in ASP.NET
Type or Namespace Name Does Not Exist
Converting a Base 64 String to an Image and Saving It
How to Check the Number of Bytes Consumed by a Structure
How to Do Pagination in ASP.NET MVC
What Is the Use of Observablecollection in .Net
Generic Methods in .Net Cannot Have Their Return Types Inferred. Why