Reading a file in real-time using Node.js

后端 未结 4 392
無奈伤痛
無奈伤痛 2020-12-05 05:30

I need to work out the best way to read data that is being written to a file, using node.js, in real time. Trouble is, Node is a fast moving ship which makes finding the bes

4条回答
  •  有刺的猬
    2020-12-05 06:13

    I took the answer from @hasanyasin and wrapped it up into a modular promise. The basic idea is that you pass a file and a handler function that does something with the stringified-buffer that is read from the file. If the handler function returns true, then the file will stop being read. You can also set a timeout that will kill reading if the handler doesn't return true fast enough.

    The promiser will return true if the resolve() was called due to timeout, otherwise it will return false.

    See the bottom for usage example.

    // https://stackoverflow.com/a/11233045
    
    var fs = require('fs');
    var Promise = require('promise');
    
    class liveReaderPromiseMe {
        constructor(file, buffStringHandler, opts) {
            /*
                var opts = {
                    starting_position: 0,
                    byte_size: 256,
                    check_for_bytes_every_ms: 3000,
                    no_handler_resolution_timeout_ms: null
                };
            */
    
            if (file == null) {
                throw new Error("file arg must be present");
            } else {
                this.file = file;
            }
    
            if (buffStringHandler == null) {
                throw new Error("buffStringHandler arg must be present");
            } else {
                this.buffStringHandler = buffStringHandler;
            }
    
            if (opts == null) {
                opts = {};
            }
    
            if (opts.starting_position == null) {
                this.current_position = 0;
            } else {
                this.current_position = opts.starting_position;
            }
    
            if (opts.byte_size == null) {
                this.byte_size = 256;
            } else {
                this.byte_size = opts.byte_size;
            }
    
            if (opts.check_for_bytes_every_ms == null) {
                this.check_for_bytes_every_ms = 3000;
            } else {
                this.check_for_bytes_every_ms = opts.check_for_bytes_every_ms;
            }
    
            if (opts.no_handler_resolution_timeout_ms == null) {
                this.no_handler_resolution_timeout_ms = null;
            } else {
                this.no_handler_resolution_timeout_ms = opts.no_handler_resolution_timeout_ms;
            }
        }
    
    
        startHandlerTimeout() {
            if (this.no_handler_resolution_timeout_ms && (this._handlerTimer == null)) {
                var that = this;
                this._handlerTimer = setTimeout(
                    function() {
                        that._is_handler_timed_out = true;
                    },
                    this.no_handler_resolution_timeout_ms
                );
            }
        }
    
        clearHandlerTimeout() {
            if (this._handlerTimer != null) {
                clearTimeout(this._handlerTimer);
                this._handlerTimer = null;
            }
            this._is_handler_timed_out = false;
        }
    
        isHandlerTimedOut() {
            return !!this._is_handler_timed_out;
        }
    
    
        fsReadCallback(err, bytecount, buff) {
            try {
                if (err) {
                    throw err;
                } else {
                    this.current_position += bytecount;
                    var buff_str = buff.toString('utf-8', 0, bytecount);
    
                    var that = this;
    
                    Promise.resolve().then(function() {
                        return that.buffStringHandler(buff_str);
                    }).then(function(is_handler_resolved) {
                        if (is_handler_resolved) {
                            that.resolve(false);
                        } else {
                            process.nextTick(that.doReading.bind(that));
                        }
                    }).catch(function(err) {
                        that.reject(err);
                    });
                }
            } catch(err) {
                this.reject(err);
            }
        }
    
        fsRead(bytecount) {
            fs.read(
                this.file,
                new Buffer(bytecount),
                0,
                bytecount,
                this.current_position,
                this.fsReadCallback.bind(this)
            );
        }
    
        doReading() {
            if (this.isHandlerTimedOut()) {
                return this.resolve(true);
            } 
    
            var max_next_bytes = fs.fstatSync(this.file).size - this.current_position;
            if (max_next_bytes) {
                this.fsRead( (this.byte_size > max_next_bytes) ? max_next_bytes : this.byte_size );
            } else {
                setTimeout(this.doReading.bind(this), this.check_for_bytes_every_ms);
            }
        }
    
    
        promiser() {
            var that = this;
            return new Promise(function(resolve, reject) {
                that.resolve = resolve;
                that.reject = reject;
                that.doReading();
                that.startHandlerTimeout();
            }).then(function(was_resolved_by_timeout) {
                that.clearHandlerTimeout();
                return was_resolved_by_timeout;
            });
        }
    }
    
    
    module.exports = function(file, buffStringHandler, opts) {
        try {
            var live_reader = new liveReaderPromiseMe(file, buffStringHandler, opts);
            return live_reader.promiser();
        } catch(err) {
            return Promise.reject(err);
        }
    };
    

    Then use the above code like this:

    var fs = require('fs');
    var path = require('path');
    var Promise = require('promise');
    var liveReadAppendingFilePromiser = require('./path/to/liveReadAppendingFilePromiser');
    
    var ending_str = '_THIS_IS_THE_END_';
    var test_path = path.join('E:/tmp/test.txt');
    
    var s_list = [];
    var buffStringHandler = function(s) {
        s_list.push(s);
        var tmp = s_list.join('');
        if (-1 !== tmp.indexOf(ending_str)) {
            // if this return never occurs, then the file will be read until no_handler_resolution_timeout_ms
            // by default, no_handler_resolution_timeout_ms is null, so read will continue forever until this function returns something that evaluates to true
            return true;
            // you can also return a promise:
            //  return Promise.resolve().then(function() { return true; } );
        }
    };
    
    var appender = fs.openSync(test_path, 'a');
    try {
        var reader = fs.openSync(test_path, 'r');
        try {
            var options = {
                starting_position: 0,
                byte_size: 256,
                check_for_bytes_every_ms: 3000,
                no_handler_resolution_timeout_ms: 10000,
            };
    
            liveReadAppendingFilePromiser(reader, buffStringHandler, options)
            .then(function(did_reader_time_out) {
                console.log('reader timed out: ', did_reader_time_out);
                console.log(s_list.join(''));
            }).catch(function(err) {
                console.error('bad stuff: ', err);
            }).then(function() {
                fs.closeSync(appender);
                fs.closeSync(reader);
            });
    
            fs.write(appender, '\ncheck it out, I am a string');
            fs.write(appender, '\nwho killed kenny');
            //fs.write(appender, ending_str);
        } catch(err) {
            fs.closeSync(reader);
            console.log('err1');
            throw err;
        }
    } catch(err) {
        fs.closeSync(appender);
            console.log('err2');
        throw err;
    }
    

提交回复
热议问题