4

我想将多个文件一个接一个地流式传输到浏览器。举例来说,请考虑将多个 CSS 文件串联在一起交付。

我正在使用的代码是:

var directory = path.join(__dirname, 'css');
fs.readdir(directory, function (err, files) {
  async.eachSeries(files, function (file, callback) {
    if (!endsWith(file, '.css')) { return callback(); } // (1)

    var currentFile = path.join(directory, file);
    fs.stat(currentFile, function (err, stats) {
      if (stats.isDirectory()) { return callback(); } // (2)

      var stream = fs.createReadStream(currentFile).on('end', function () {
        callback(); // (3)
      });
      stream.pipe(res, { end: false }); // (4)
    });
  }, function () {
    res.end(); // (5)
  });
});

这个想法是我

  1. 过滤掉所有没有文件扩展名的文件.css
  2. 过滤掉所有目录。
  3. 完全读取文件后继续下一个文件。
  4. 将每个文件通过管道传输到响应流而不关闭它。
  5. 一旦所有文件都通过管道传输,就结束响应流。

问题是只有第一个.css文件被管道传输,并且所有剩余的文件都丢失了。就好像(3)会在第一个(4)之后直接跳转到(5)。

有趣的是,如果我将第 (4) 行替换为

stream.on('data', function (data) {
  console.log(data.toString('utf8'));
});

一切都按预期工作:我看到多个文件。如果我然后将此代码更改为

stream.on('data', function (data) {
  res.write(data.toString('utf8'));
});

所有文件都希望第一个再次丢失。

我究竟做错了什么?

PS:错误发生在使用 Node.js 0.8.7 以及使用 0.8.22 时。

更新

好的,如果您将代码更改如下:

var directory = path.join(__dirname, 'css');
fs.readdir(directory, function (err, files) {
  var concatenated = '';
  async.eachSeries(files, function (file, callback) {
    if (!endsWith(file, '.css')) { return callback(); }

    var currentFile = path.join(directory, file);
    fs.stat(currentFile, function (err, stats) {
      if (stats.isDirectory()) { return callback(); }

      var stream = fs.createReadStream(currentFile).on('end', function () {
        callback();
      }).on('data', function (data) { concatenated += data.toString('utf8'); });
    });
  }, function () {
    res.write(concatenated);
    res.end();
  });
});

但为什么?为什么我不能res.write多次调用而不是先总结所有块,然后一次将它们全部写入?

4

3 回答 3

3

还可以考虑使用multistream,它允许您一个接一个地组合和发出多个流。

于 2018-08-31T10:48:26.717 回答
2

代码非常好,是单元测试出错了......

修复了这个问题,现在它就像一个魅力:-)

于 2013-03-09T21:42:26.397 回答
0

可以帮助别人:

const fs = require("fs");
const pth = require("path");

let readerStream1 = fs.createReadStream(pth.join(__dirname, "a.txt"));
let readerStream2 = fs.createReadStream(pth.join(__dirname, "b.txt"));
let writerStream = fs.createWriteStream(pth.join(__dirname, "c.txt"));

//only readable streams have "pipe" method
readerStream1.pipe(writerStream);
readerStream2.pipe(writerStream);

我还检查了Rocco 的答案,它的作用就像一个魅力:

//npm i --save multistream

const multi = require('multistream');
const fs = require('fs');
const pth = require("path");
 
let streams = [
  fs.createReadStream(pth.join(__dirname, "a.txt")),
  fs.createReadStream(pth.join(__dirname, "b.txt"))
];
let writerStream = fs.createWriteStream(pth.join(__dirname, "c.txt"));
 
//new multi(streams).pipe(process.stdout);
new multi(streams).pipe(writerStream);

并将结果发送给客户端:

const multi = require('multistream');
const fs = require('fs');
const pth = require("path");
const exp = require("express");
const app = exp();
app.listen(3000);

app.get("/stream", (q, r) => {
  new multi([
    fs.createReadStream(pth.join(__dirname, "a.txt")),
    fs.createReadStream(pth.join(__dirname, "b.txt"))
  ]).pipe(r);
});
于 2020-12-31T12:30:05.973 回答