Node.js - 在管道响应之前检查流是否有错误

Jac*_*son 4 javascript stream node.js

在Node.js中,假设我想从某个地方读取文件并流式传输响应(例如,使用文件系统fs.createReadStream()).

application.get('/files/:id', function (request, response) {
    var readStream = fs.createReadStream('/saved-files/' + request.params.id);
    var mimeType = getMimeTypeSomehow(request.params.id);
    if (mimeType === 'application/pdf') {
        response.set('Content-Range', ...);
        response.status(206);
    } else {
        response.status(200);
    }
    readStream.pipe(response);
});
Run Code Online (Sandbox Code Playgroud)

但是,我想在发送响应头之前检测流是否有错误.我怎么做?

伪代码:

application.get('/files/:id', function (request, response) {
    var readStream = fs.createReadStream('/saved-files/' + request.params.id);
    readStream.on('ready', function () {
        var mimeType = getMimeTypeSomehow(request.params.id);
        if (mimeType === 'application/pdf') {
            response.set('Content-Range', ...);
            response.status(206);
        } else {
            response.status(200);
        }
        readStream.pipe(response);
    });
    readStream.on('error', function () {
        response.status(404).end();
    });
});
Run Code Online (Sandbox Code Playgroud)

has*_*sin 6

当readStream结束或出错时,写入流结束.您可以通过end:false在管道期间传递并手动结束写入流来阻止此默认行为.

因此,即使发生错误,您的写入流仍然是打开的,您可以在error回调中使用writestream执行其他操作(例如,发送404状态).

var readStream = fs.createReadStream('/saved-files/' + request.params.id);
readStream.on('error', function () {
    res.status(404).end();
});
readStream.on('end', function(){
  res.end(); //end write stream manually when readstream ends
})
readStream.pipe(res,{end:false}); // prevent default behaviour
Run Code Online (Sandbox Code Playgroud)

更新1:对于文件流,您可以侦听open事件以检查文件是否可以读取:

readStream.on('open', function () {
    // set response headers and status
});
Run Code Online (Sandbox Code Playgroud)

更新2:正如OP所提到的open,其他流可能没有事件,如果流是从节点的流模块继承的,我们可以使用以下内容.诀窍是我们手动编写数据而不是pipe()方法.这样我们就可以在开始写第一个字节之前对writable进行一些'初始化'.

所以我们once('data')首先绑定然后绑定on('data').在实际写作发生之前,将调用第一个.

readStream
.on('error',function(err) {
  res.status(404).end();
})
.once('data',function(){ 
  //will be called once and before the on('data') callback
  //so it's safe to set headers here
  res.set('Content-Type', 'text/html');
})
.on('data', function(chunk){ 
  //now start writing data 
  res.write(chunk);
})
.on('end',res.end.bind(res)); //ending writable when readable ends
Run Code Online (Sandbox Code Playgroud)