Node.js - Check if stream has error before piping response

冷暖自知 提交于 2020-07-06 11:26:20

问题


In Node.js, say that I want to read a file from somewhere and stream the response (e.g., from the filesystem using 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);
});

However, I want to detect if there is an error with the stream before sending my response headers. How do I do that?

Pseudocode:

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

回答1:


Write stream is ended when readStream ends or has an error. You can prevent this default behaviour by passing end:false during pipe and end the write stream manually.

So even if the error occurs, your write stream is still open and you can do other stuff(e.g. sending 404 status) with writestream in the error callback.

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

Update 1: For file streams, you can listen for open event to check if the file is ready to read:

readStream.on('open', function () {
    // set response headers and status
});

Update 2: As OP mentioned there may be no open event for other streams, we may use the following if the stream is inherited from node's stream module. The trick is we write the data manually instead of pipe() method. That way we can do some 'initialization' on writable before starting to write first byte.

So we bind once('data') first and then bind on('data'). First one will be called before actual writing is happened.

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


来源:https://stackoverflow.com/questions/31844316/node-js-check-if-stream-has-error-before-piping-response

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!