问题
How to utilize Angular HTTPClient's progress
event to show progress in percentage of Get request which does not necessarily a file request?
Currently HTTPClient's progress
event fires after request completion. I am hoping to work with Content-Length
at back end and determine percentage of content loaded at front end.
I am loading a large amount of rows for a grid and need to show incremental progress on UI. Is it possible?
回答1:
I know this question is older but, i stumbled upon this while searching for an answer to a similar problem and since there is no accepted answer i post my solution.
I recently implemented a generic way to display a progress bar for every request no matter the type in angular 8.
First i created a HttpInterceptor
which would automatically intercept every http call where the reportProgress
option is set to true
.
@Injectable()
export class HttpProgressInterceptor implements HttpInterceptor {
constructor(
private spinnerService: SpinnerService // my personal service for the progress bar - replace with your own
) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.reportProgress) {
// only intercept when the request is configured to report its progress
return next.handle(req).pipe(
tap((event: HttpEvent<any>) => {
if (event.type === HttpEventType.DownloadProgress) {
// here we get the updated progress values, call your service or what ever here
this.spinnerService.updateGlobalProgress(Math.round(event.loaded / event.total * 100)); // display & update progress bar
} else if (event.type === HttpEventType.Response) {
this.spinnerService.updateGlobalProgress(null); // hide progress bar
}
}, error => {
this.spinnerService.updateGlobalProgress(null); // hide progress bar
})
);
} else {
return next.handle(req);
}
}
}
You need to register this interceptor in your module
of course:
@NgModule({
declarations: [
AppComponent,
...
],
imports: [
BrowserModule,
...
RouterModule.forRoot(appRoutes)
],
providers: [
...
{ provide: HTTP_INTERCEPTORS, useClass: HttpProgressInterceptor, multi: true },
...}
],
bootstrap: [AppComponent]
})
export class AppModule { }
Basically we're done here, only thing left is that we need to change the way we call our apis. If you want a specific request to be monitored using this interceptor you need to tell angular to report the progress on the HttpRequest:
@Injectable()
export class MyService {
constructor(
private http: HttpClient
) {}
myGetMethod() {
const url = "api/data/load/big/data";
const req = new HttpRequest("GET", url, {
reportProgress: true // this is important!
});
return this.http.request(req);
}
}
This way of calling the httpClient
api deliveres a different object when calling .subscribe
so we need to take care of that when calling the myGetMethod()
:
ngOnInit() {
this.myService.myGetMethod().subscribe((event: HttpEvent<any>) => {
if (event.type === HttpEventType.Response) {
const responseData = event.body;
console.dir(responseData); // do something with the response
}
});
}
We could also listen here for the HttpEventType.DownloadProgress
event and update the progress values inside this component - but that was not the point of my example.
Hint: if you encounter the problem that event.total
is undefined - you must check whether your REST backend REALLY is providing the Content-Length
header - if this header is missing, you will not be able to calculate the progress!
anyway, i hope this will help somebody someday 😉
回答2:
What about this:
import { HttpEventType, HttpClient, HttpRequest} from '@angular/common/http';
...
const request = new HttpRequest('GET', url, {
reportProgress: true
});
http.request(request).subscribe(event => {
// progress
if (event.type === HttpEventType.DownloadProgress) {
console.log(event.loaded, event.total);
// event.loaded = bytes transfered
// event.total = "Content-Length", set by the server
const percentage = 100 / event.total * event.loaded;
console.log(percentage);
}
// finished
if (event.type === HttpEventType.Response) {
console.log(event.body);
}
})
回答3:
You can use this
const req = new HttpRequest('POST', '/upload/file', file, {
reportProgress: true
});
Next, pass this request object to the HttpClient.request() method, which returns an Observable of HttpEvents, the same events processed by interceptors:
// The `HttpClient.request` API produces a raw event stream
// which includes start (sent), progress, and response events.
return this.http.request(req).pipe(
map(event => this.getEventMessage(event, file)),
tap(message => this.showProgress(message)),
last(), // return last (completed) message to caller
catchError(this.handleError(file))
);
Finally you can use that to help
/** Return distinct message for sent, upload progress, & response events */
private getEventMessage(event: HttpEvent<any>, file: File) {
switch (event.type) {
case HttpEventType.Sent:
return `Uploading file "${file.name}" of size ${file.size}.`;
case HttpEventType.UploadProgress:
// Compute and show the % done:
const percentDone = Math.round(100 * event.loaded / event.total);
return `File "${file.name}" is ${percentDone}% uploaded.`;
case HttpEventType.Response:
return `File "${file.name}" was completely uploaded!`;
default:
return `File "${file.name}" surprising upload event: ${event.type}.`;
}
}
来源:https://stackoverflow.com/questions/48461386/angular-rxjs-how-to-monitor-progress-of-http-get-request-not-file