Angular 对HttpClient结果进行长轮询并将其流式传输到CSV文件

Angular 对HttpClient结果进行长轮询并将其流式传输到CSV文件,angular,rxjs,httpclient,Angular,Rxjs,Httpclient,问题1: 我如何实现相同的行为?但不是可观察的。间隔 它将被回调调用 例如: 我有5000ms的间隔,但是我的服务器速度非常慢,而且在5000ms之后它没有返回结果。但是下一个调用是在5000ms之后调用的。我不想那样。我希望在结果从服务器返回后,它将调用下一个调用 问题2: 如何将结果立即流式传输到csv文件,而不逐个创建多个文件。对于当前的实现,我使用FileSaver,它在IE11中工作。我想继续使用它。 是否有一种方法可以将数据流式传输到文件中,而不是将其收集到数组中,因为我有大量的数据

问题1: 我如何实现相同的行为?但不是
可观察的。间隔

它将被回调调用

例如: 我有
5000ms
的间隔,但是我的服务器速度非常慢,而且在
5000ms
之后它没有返回结果。但是下一个调用是在
5000ms
之后调用的。我不想那样。我希望在结果从服务器返回后,它将调用下一个调用

问题2: 如何将结果立即流式传输到csv文件,而不逐个创建多个文件。对于当前的实现,我使用
FileSaver
,它在
IE11
中工作。我想继续使用它。 是否有一种方法可以将数据流式传输到文件中,而不是将其收集到数组中,因为我有大量的数据集。像1百万排这样。。。 例如:

const progress = Observable.interval(1000)
  .switchMap(() => this.messageService.getResults(query))
  .map(messageResult => messageResult)
  .subscribe((data: MessagesResult) => {
    inProcess = true;
    if (!data.isMoreResults && data.auditMessageList.length === 0) {
      this.fileSaver.save(`result.csv`, csvData);
      inProcess = false;
      this.logger.info('Download file finished...');
      progress.unsubscribe();
    }
    const start = this.filterModel.offset * this.filterModel.limit;
    const rows = [...csvData];
    rows.splice(start, 0, ...data.auditMessageList);
    csvData = rows;
    if (inProcess) {
      this.logger.info('Exporting in progress...');
    }
    query.offset++;
  }, error => this.logger.error(error));

}

正如您使用
Observable发现的那样。interval
不会“等待”流的其余部分

我通常使用
repeatWhen
delay

const progress = Observable.defer(() => this.messageService.getResults(query))
  .repeatWhen(notifications => notifications.delay(1000)) 
  ...
下面是一个工作示例:

我不太理解你们其他人的代码

不要使用
progress.unsubscribe()
订阅
方法中。取而代之的是考虑使用或两者都将完成对你的观察。

.takeWhile(data => data.isMoreResults  data.auditMessageList.length > 0)
此外,还可以通过使用或来缓冲结果

副作用最好由操作员处理

关于第二个问题-我不知道-您应该能够从服务器流csv。如果您无法修改服务器,可能其他人会知道如何在客户端上修改…

问题1 下面是一个实现函数的示例,该函数在收到响应时调用自身

后端:

**Request N. 0**
[Back] : received a request from the front
[Back] : Responding after 5482 ms
[Front] : received response from server. State : open
[Front] : doing some sync processing
**Request N. 1**
[Back] : received a request from the front
[Back] : Responding after 7489 ms
[Front] : received response from server. State : open
[Front] : doing some sync processing
**Request N. 2**
[Back] : received a request from the front
[Back] : Responding after 9627 ms
[Front] : received response from server. State : open
[Front] : doing some sync processing
**Request N. 3**
[Back] : received a request from the front
[Back] : Responding after 5806 ms
[Front] : received response from server. State : closed
[Front] : doing some sync processing
saving to csv :
current_request;state
1;open
2;open
3;open
  • 模拟在5到10秒内响应的慢速后端
  • 在每次响应时,服务器都会给出当前的
    请求编号
    状态
  • 对于前3个响应,
    状态
    活动
    ,之后,
    状态
    关闭
  • 代码:

    正面:

    **Request N. 0**
    [Back] : received a request from the front
    [Back] : Responding after 5482 ms
    [Front] : received response from server. State : open
    [Front] : doing some sync processing
    **Request N. 1**
    [Back] : received a request from the front
    [Back] : Responding after 7489 ms
    [Front] : received response from server. State : open
    [Front] : doing some sync processing
    **Request N. 2**
    [Back] : received a request from the front
    [Back] : Responding after 9627 ms
    [Front] : received response from server. State : open
    [Front] : doing some sync processing
    **Request N. 3**
    [Back] : received a request from the front
    [Back] : Responding after 5806 ms
    [Front] : received response from server. State : closed
    [Front] : doing some sync processing
    saving to csv :
    current_request;state
    1;open
    2;open
    3;open
    
    这基本上是您的组件

    class Frontend {
    
      isPollingActivated = true;
      responses = [];
    
    
      constructor(private backendService) {
        this.backendService = new SlowBackend(); // connection to backend
        this.requestOnRegularBasis();
      }
    
      requestOnRegularBasis() {
        if (!this.isPollingActivated)
          return;
    
        this.backendService.getStuff()
          .subscribe(response => {
            console.log(`[Front] : received response from server. State : ${response.state}`);
    
            // Choose one of the following blocks, comment the other according to what you need
    
            // Block 1 : Sync processing example
            console.log(`[Front] : doing some sync processing`);
            this.doSomeSyncProcessing(response);
            this.requestOnRegularBasis();
    
            // Block 2 : Async processing example
            // console.log(`[Front] : doing some async processing`);
            // this.doSomeAsyncProcessing(response)
            //    .subscribe(this.requestOnRegularBasis);
    
          })
      }
    
      private doSomeSyncProcessing(response){
        if(response.state == 'closed'){
          this.isPollingActivated = false; // stop polling
          this.saveDataToCsv();
        }
        else
          this.responses.push(Object.values(response).join(';')) // csv line separated by ';'
      }
    
      private saveDataToCsv(){
        const headers = ['current_request;state']
        this.responses = headers.concat(this.responses)
        console.log('saving to csv : ', this.responses.join('\n'));
    
        // Uncomment this to use FileSaver API
        /*
        const blob = new Blob(headers.concat(this.responses), {type: "text/csv;charset=utf-8"});
        saveAs(blob, "my_responses.csv");*
        */
      }
    
      private doSomeAsyncProcessing(response){
        return Observable.timer(1000).map(() => this.doSomeSyncProcessing(response));
      }
    
    }
    
    输出:

    **Request N. 0**
    [Back] : received a request from the front
    [Back] : Responding after 5482 ms
    [Front] : received response from server. State : open
    [Front] : doing some sync processing
    **Request N. 1**
    [Back] : received a request from the front
    [Back] : Responding after 7489 ms
    [Front] : received response from server. State : open
    [Front] : doing some sync processing
    **Request N. 2**
    [Back] : received a request from the front
    [Back] : Responding after 9627 ms
    [Front] : received response from server. State : open
    [Front] : doing some sync processing
    **Request N. 3**
    [Back] : received a request from the front
    [Back] : Responding after 5806 ms
    [Front] : received response from server. State : closed
    [Front] : doing some sync processing
    saving to csv :
    current_request;state
    1;open
    2;open
    3;open
    
    问题2 你不能

    至少不使用
    FileSaver
    。因为它不支持逐块编写。当实例化
    Blob
    时,必须 准备好所有数据。 有些库支持块,但它们要么是用于服务器端(例如node.js),要么是非常特定于浏览器的

    选中此项:

    注:

    如果您试图使用js在客户机上存储一个1M行csv,那么该架构可能有问题。 因为这不是浏览器的常见用例。客户机应该有较弱的机器,因此接收处理, 轻巧,易于解析信息。因此,您可以在服务器端构建csv,它将 拥有写入流文件的所有权限,并具有适当的处理/内存容量

    演示:问题1

    演示:如何下载blob?

    
    var blob=new blob([“你好,世界!”],{type:“text/plain;charset=utf-8”});
    saveAs(blob,“helloworld.txt”);
    
    问题1:

    **Request N. 0**
    [Back] : received a request from the front
    [Back] : Responding after 5482 ms
    [Front] : received response from server. State : open
    [Front] : doing some sync processing
    **Request N. 1**
    [Back] : received a request from the front
    [Back] : Responding after 7489 ms
    [Front] : received response from server. State : open
    [Front] : doing some sync processing
    **Request N. 2**
    [Back] : received a request from the front
    [Back] : Responding after 9627 ms
    [Front] : received response from server. State : open
    [Front] : doing some sync processing
    **Request N. 3**
    [Back] : received a request from the front
    [Back] : Responding after 5806 ms
    [Front] : received response from server. State : closed
    [Front] : doing some sync processing
    saving to csv :
    current_request;state
    1;open
    2;open
    3;open
    
    使用
    forkJoin
    。它将等待所有观察到的结果完成。 与
    延迟(5000)
    组合使用时,最短时间为5s。如果API响应在5s之前没有返回,它仍然会等待结果返回()

    const stream1$=of(1).管道(
    延迟(5000)
    );
    const intervalTime=Math.random()*5000+5000
    //替换为您的API流
    const stream2$=of(间隔时间)。管道(
    延迟(间隔时间)
    );
    forkJoin(stream1$,stream2$)
    .订阅(([[uuS2])=>{
    控制台日志(s2);
    })
    
    问题2:

    **Request N. 0**
    [Back] : received a request from the front
    [Back] : Responding after 5482 ms
    [Front] : received response from server. State : open
    [Front] : doing some sync processing
    **Request N. 1**
    [Back] : received a request from the front
    [Back] : Responding after 7489 ms
    [Front] : received response from server. State : open
    [Front] : doing some sync processing
    **Request N. 2**
    [Back] : received a request from the front
    [Back] : Responding after 9627 ms
    [Front] : received response from server. State : open
    [Front] : doing some sync processing
    **Request N. 3**
    [Back] : received a request from the front
    [Back] : Responding after 5806 ms
    [Front] : received response from server. State : closed
    [Front] : doing some sync processing
    saving to csv :
    current_request;state
    1;open
    2;open
    3;open
    
    如果文件很大,您应该让Web浏览器处理它。最好将文件保存在服务器中,然后返回下载链接。对于小文件,性能不是问题。您可以将文件数据存储在RAM中,然后保存文件一次

    编辑:如果文件较大,FileSaver开发人员建议使用。你应该看看

    StreamSaver.js采用不同的方法。您现在可以直接创建一个可写流到文件系统,而不是将数据保存在客户端存储或内存中(我不是说chromes沙盒文件系统)

    StreamSaver.js是在客户端保存流的解决方案。它非常适合需要保存在客户端创建的大量数据的Web应用程序,而在客户端,RAM非常有限,例如在移动设备上


    @M1CH4在第一次请求后死亡。如何使其运行直到达到终止状态?@iamstapper老实说,我不理解您的代码-我已经写了一些可能有用的建议。可能尝试从问题中删除以
    this.
    开头的所有内容(使其更加独立)或创建JSFIDLE假设我正在调用API,我不知道max iteration,也没有固定的超时。假设服务器速度非常慢,每次超时后都会返回响应。在我的示例中,没有固定的超时。它在5s到10s之间变化,但是如果它在1s到1min之间变化,它将是相同的。如果服务器在大约150秒后没有响应,连接将断开,如果使用http请求,则无法修复此问题。在这种情况下,您必须打开websocket,或者使用类似firebase的技术。至于最大迭代次数,这不是问题,您可以用5000次迭代替换3次。问题是,一旦blob(或csv)的大小达到600Mb左右,你就必须存储它,并重置你的响应数组。嗯,有趣的是,我在帖子中读到了你在这里给出的js块,它是