angular2 http安魂曲处理大量响应


angular2 http requist handling chunke of responses

当我试验angular2时,出现了一个小障碍:

  • 我有php代码,它使用"ob_flush"返回大块响应。

  • 在前端,我成功地发出了"xhr=XMLHttpRequest"请求,并接收到响应,并使用"xhr.onprogress()"answers"xr.onreadystatechange()"进行处理。

现在当我试图使用angular2http.get()获得相同的功能时,我无法在结果从服务器到达时输出!相反,在接收到最后一个响应之后的过程结束时,通过角度来显示结果
我认为rxjs Observer对象正在缓冲响应!。

那么我该如何改变这种行为呢?

这是我的php代码,testing.php

echo date('H:i:s')." Loading data!";
ob_flush();
flush();
sleep(5);
echo "Ready to run!";

这是我的angular2代码:

template: `
    <div>
        <h3>experimenting!</h3>
        <button (click)="callServer()">run the test</button>
        <div>the server says: {{msg}}</div>
    </div>`
export class AppComponent {
  msg:any;
  constructor (private http:Http){}
  callServer(){
    this.http.get("localhost/testing.php")
             .subscribe(res=> this.msg= res.text());
  }
}

我运行此代码时,它会在5秒钟后显示:(19:59:47正在加载数据!准备运行!)

  • 它应该立即输出:(19:59:47加载数据!)

  • 5秒钟后,将上一条消息替换为:(准备运行!)

您需要扩展BrowserXhr类来执行此操作,以便配置所使用的低级别XHR对象:

@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 })
]);

有关更多详细信息,请参阅此问题:

  • Angular 2 HTTP进度条

在研究了rxjs并阅读了Angular2源代码后,我提出了这个解决方案

我发现做custom_backend更好,我认为这是angular Dev团队推荐的方法。

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);
  }
}

在主要组件中,我们必须提供这样的自定义烘焙:

providers:  [
    HTTP_PROVIDERS,
    PostSrevice,
    MyBackend,
    provide(XHRBackend, {useExisting:MyBackend})
]

现在当我们使用http.get()时,它将返回的Observable