简体   繁体   English

获得有关流管道的不完整数据以进行快速响应

[英]Got incomplete data on stream piping to an express response

Need to convert a DB table to a csv report.需要将数据库表转换为 csv 报告。
If I immediately unload the entire tablet with one query then the application crashes because the memory runs out.如果我立即用一个查询卸载整个平板电脑,那么应用程序会因为内存耗尽而崩溃。 I decided to query data from the table in portions of 100 rows, convert each row into a line of the report and write it into a stream that is piped with an express response.我决定以 100 行为一组从表中查询数据,将每一行转换为报表的一行,然后将其写入一个通过快速响应管道传输的流。

All this happens nearly like this:这一切几乎是这样发生的:

  1. DB query数据库查询

    const select100Users = (maxUserCreationDateStr) => { return db.query(` SELECT * FROM users WHERE created_at < to_timestamp(${maxUserCreationDateStr}) ORDER BY created_at DESC LIMIT 100`); }
  2. stream initialisation流初始化

    const { PassThrough } = require('stream'); const getUserReportStream = () => { const stream = new PassThrough(); writeUserReport(stream).catch((e) => stream.emit('error', e)); return stream; };
  3. piping the stream with an express response用快速响应管道传输流

    app.get('/report', (req, res) => { const stream = getUserReportStream(); res.setHeader('Content-Type', 'application/vnd.ms-excel'); res.setHeader(`Content-Disposition', 'attachment; filename="${ filename }"`); stream.pipe(res); });
  4. and finally how do I write data to the stream最后我如何将数据写入流

    const writeUserReport(stream) => { let maxUserCreationDateGlobal = Math.trunc(Date.now() / 1000); let flag = true; stream.write(USER_REPORT_HEADER); while (flag) { const rows100 = await select100Users(maxUserCreationDateGlobal); console.log(rows100.length); if (rows100.length === 0) { flag = false; } else { let maxUserCreationDate = maxUserCreationDateGlobal; const users100 = await Promise.all( rows100.map((r) => { const created_at = r.created_at; const createdAt = new Date(created_at); if (created_at && createdAt.toString() !== 'Invalid Date') { const createdAtNumber = Math.trunc(createdAt.valueOf() / 1000); maxUserCreationDate = Math.min(maxUserCreationDate, createdAtNumber); } return mapUser(r); // returns a promise }) ); users100.forEach((u) => stream.write(generateCsvRowFromUser(u))); maxUserCreationDateGlobal = maxUserCreationDate; if (rows100.length < 100) { flag = false; console.log('***'); } } } console.log('end'); stream.end(); };

as a result I see this output in the console:结果,我在控制台中看到了这个输出:

100 // 100
100 // 200
100 // 300
100 // 400
100 // 500
87  // 587
***
end

But in the downloaded file I get 401 lines (the first one with USER_REPORT_HEADER).但是在下载的文件中,我得到了 401 行(第一行带有 USER_REPORT_HEADER)。 It feels like stream.end() closes the stream before all values are read from it.感觉就像stream.end()在读取所有值之前关闭了流。

I tried using BehaviorSubject from rxjs instead of PassThrough in a similar way - the result is the same..我尝试以类似的方式使用 rxjs 中的 BehaviorSubject 而不是 PassThrough - 结果是相同的..

How can I wait for reading from the stream of all the data that I wrote there?我怎样才能等待从我在那里写的所有数据的流中读取?
Or maybe someone can recommend an alternative way to solve this problem.或者也许有人可以推荐一种替代方法来解决这个问题。

stream.write expects you to pass a callback as a second (or third parameter), to know when the write operation did finish. stream.write期望您将回调作为第二个(或第三个参数)传递,以了解写操作何时完成。 You can't call write again unless the previous write operation is finished.除非之前的写操作完成,否则不能再次调用 write。

So in general I'd suggest to make this whole function async and every time you call stream.write you wrap it into a Promise like所以总的来说,我建议让整个函数异步,每次调用stream.write时,你都会将它包装成一个 Promise

await new Promise((resolve, reject) => stream.write(data, (error) => {
   if (error) {
      reject(error);
      return;
   }
   resolve();
});

Obviously it would make sense to extract this to some method.显然,将其提取到某种方法是有意义的。

EDIT : Additionally I don't think that's the actual problem.编辑:另外,我认为这不是实际问题。 I assume your http connection is just timing out before all the fetching is completed, so the server will eventually close the stream once the timeout deadline is met.我假设您的 http 连接只是在所有获取完成之前超时,因此一旦达到超时期限,服务器最终将关闭流。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM