【问题标题】:Angular 2 HTTP Progress barAngular 2 HTTP 进度条
【发布时间】:2016-09-06 15:15:41
【问题描述】:

目前在 Angular 2 中是否有一种方法可以使用 angular2/http 模块检索 ajax 调用的进度(即完成百分比)?

我使用以下代码进行 HTTP 调用:

        let body = JSON.stringify(params);
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });
        this.http.post(url, body, options)
            .timeout(10000, new Error('Timeout exceeded during login'))
            .toPromise()
            .then((res) => {
                ...
            }).catch((err) => {
                ...
            });

目标是编写一个同步系统。帖子会返回大量数据,我想告诉用户同步需要多长时间。

【问题讨论】:

    标签: ajax http angular ionic2


    【解决方案1】:

    目前(从 v. 4.3.0 开始,当使用来自 @ngular/common/http 的新 HttpClient 时)Angular 提供开箱即用的监听进度。您只需要创建 HTTPRequest 对象,如下所示:

    import { HttpRequest } from '@angular/common/http';
    ...
    
    const req = new HttpRequest('POST', '/upload/file', file, {
      reportProgress: true,
    });
    

    当您订阅请求时,您将在每个进度事件上调用订阅:

    http.request(req).subscribe(event => {
      // Via this API, you get access to the raw event stream.
      // Look for upload progress events.
      if (event.type === HttpEventType.UploadProgress) {
        // This is an upload progress event. Compute and show the % done:
        const percentDone = Math.round(100 * event.loaded / event.total);
        console.log(`File is ${percentDone}% uploaded.`);
      } else if (event instanceof HttpResponse) {
        console.log('File is completely uploaded!');
      }
    });
    

    更多信息here

    【讨论】:

    【解决方案2】:

    您可以利用 XHR 提供的 onprogress 事件(请参阅此 plunkr:http://plnkr.co/edit/8MDO2GsCGiOJd2y2XbQk?p=preview)。

    这允许获取有关下载进度的提示。 Angular2 不支持开箱即用,但您可以通过扩展 BrowserXhr 类来插入它:

    @Injectable()
    export class CustomBrowserXhr extends BrowserXhr {
      constructor(private service:ProgressService) {}
      build(): any {
        let xhr = super.build();
        xhr.onprogress = (event) => {
          service.progressEventObservable.next(event);
        };
        return <any>(xhr);
      }
    }
    

    并使用扩展覆盖BrowserXhr 提供程序:

    bootstrap(AppComponent, [
      HTTP_PROVIDERS,
      provide(BrowserXhr, { useClass: CustomBrowserXhr })
    ]);
    

    查看这个问题了解更多详情:

    【讨论】:

    • 我希望我可以避免使用 XHRRequest 实现 :) 但对此无能为力 ;)
    • 事实上,Angular2 的 HTTP 支持中(还)不支持它,所以你需要显式依赖 XHR...
    【解决方案3】:

    当您在 angular2 中创建 http cals 时,它会返回一个类型为 Response 的 Observable,此响应是在名为 XHRConnection 的类中创建的,所有的魔法都发生在这里。

    XHRConnection 通过监听 XMLHttpRequest 的加载事件来构建响应,这意味着它会在请求结束时返回一个响应。

    现在为了能够改变这种行为,我们需要让我们的连接类监听 progress 事件。

    所以我们需要创建自定义的 Connection 类,来处理我们认为合适的响应。

    我是这样做的, 请注意,我的 php API 在单个请求中返回多个响应,并且这些响应是纯字符串。

    my_backend.ts

    import {Injectable} from "angular2/core";
    import {Observable} from "rxjs/Observable";
    import {Observer} from "rxjs/Observer";
    import {Connection,ConnectionBackend} from "angular2/src/http/interfaces";
    import {ReadyState, RequestMethod, ResponseType} from "angular2/src/http/enums";
    import {ResponseOptions} from "angular2/src/http/base_response_options";
    import {Request} from "angular2/src/http/static_request";
    import {Response} from "angular2/src/http/static_response";
    import {BrowserXhr} from "angular2/src/http/backends/browser_xhr";
    import {Headers} from 'angular2/src/http/headers';
    import {isPresent} from 'angular2/src/facade/lang';
    import {getResponseURL, isSuccess} from "angular2/src/http/http_utils"
    
    export class MyConnection implements Connection { 
        readyState: ReadyState;
        request: Request;
        response: Observable<Response>;
    
        constructor(req: Request, browserXHR: BrowserXhr, baseResponseOptions?: ResponseOptions) {       
            this.request = req;
            this.response = new Observable<Response>((responseObserver: Observer<Response>) => {
                let _xhr: XMLHttpRequest = browserXHR.build();
                _xhr.open(RequestMethod[req.method].toUpperCase(), req.url);
                // save the responses in array 
                var buffer :string[] = []; 
                // load event handler
                let onLoad = () => {
                    let body = isPresent(_xhr.response) ? _xhr.response : _xhr.responseText;
                    //_xhr.respons 1 = "Loading data!"
                    //_xhr.respons 2 = "Loading data!Ready To Receive Orders."
                    // we need to fix this proble 
                    // check if the current response text contains the previous then subtract
                    // NOTE: I think there is better approach to solve this problem.
                    buffer.push(body);
                    if(buffer.length>1){
                        body = buffer[buffer.length-1].replace(buffer[buffer.length-2],'');
                    }
                    let headers = Headers.fromResponseHeaderString(_xhr.getAllResponseHeaders());
                    let url = getResponseURL(_xhr);
                    let status: number = _xhr.status === 1223 ? 204 : _xhr.status;
                    let state:number = _xhr.readyState;
                    if (status === 0) {
                        status = body ? 200 : 0;
                    }
                    var responseOptions = new ResponseOptions({ body, status, headers, url });
                    if (isPresent(baseResponseOptions)) {
                        responseOptions = baseResponseOptions.merge(responseOptions);
                    }
                    let response = new Response(responseOptions);
                    //check for the state if not 4 then don't complete the observer
                    if(state !== 4){
                        //this will return stream of responses
                        responseObserver.next(response);
                        return;
                    }
                    else{
                        responseObserver.complete();
                        return;
                    }
                    responseObserver.error(response);
                };
                // error event handler
                let onError = (err: any) => {
                    var responseOptions = new ResponseOptions({ body: err, type: ResponseType.Error });
                    if (isPresent(baseResponseOptions)) {
                        responseOptions = baseResponseOptions.merge(responseOptions);
                    }
                    responseObserver.error(new Response(responseOptions));
                };
    
                if (isPresent(req.headers)) {
                    req.headers.forEach((values, name) => _xhr.setRequestHeader(name, values.join(',')));
                }
                _xhr.addEventListener('progress', onLoad);
                _xhr.addEventListener('load', onLoad);
                _xhr.addEventListener('error', onError);
    
                _xhr.send(this.request.text());
    
                return () => {
                    _xhr.removeEventListener('progress', onLoad);
                    _xhr.removeEventListener('load', onLoad);
                    _xhr.removeEventListener('error', onError);
                    _xhr.abort();
                };
            });
        }
    }
    @Injectable()
    export class MyBackend implements ConnectionBackend {
      constructor(private _browserXHR: BrowserXhr, private _baseResponseOptions: ResponseOptions) {}
      createConnection(request: Request):MyConnection {
        return new MyConnection(request, this._browserXHR, this._baseResponseOptions);
      }
    }
    

    而在 app.component.ts

    import {Component, provide} from 'angular2/core';
    import {HTTP_PROVIDERS,XHRBackend} from 'angular2/http';
    import {MyBackend} from './my_backend';
    @Component({
        selector: 'my-app',
        providers:  [
            HTTP_PROVIDERS,
            MyBackend,
            provide(XHRBackend, {useExisting:MyBackend})
        ]
        .
        .
        .
    

    现在调用 http.get 将返回一系列响应。

    【讨论】:

      【解决方案4】:

      @Bartek Chichoki 的回答是正确的,但它不适用于我的情况。 添加观察:“事件”对我有用

      const req = new HttpRequest('POST', '/upload/file', file, {
        reportProgress: true,
        observe: 'events'
      });
      

      希望对你有帮助

      【讨论】:

        【解决方案5】:

        强烈推荐使用这个

        https://www.npmjs.com/package/angular-progress-http

        否则乱用 xhr 会让你错过会话 cookie 和其他东西

        此外,它会更便携,更容易实现

        【讨论】:

        • 这很好用,直到你升级到 Angular 5 然后包因为它引用 OpaqueToken 而失败。
        • 我更愿意学习这项技术而不是复制别人的代码
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-04-24
        • 2019-07-12
        • 2020-03-15
        • 2019-06-01
        • 2022-01-17
        • 2014-06-06
        • 1970-01-01
        相关资源
        最近更新 更多