Merge remote branch 'felixge/file2'
This commit is contained in:
commit
5217eda1ae
80
doc/api.txt
80
doc/api.txt
@ -785,6 +785,86 @@ Objects returned from +fs.stat()+ and +fs.lstat()+ are of this type.
|
|||||||
|
|
||||||
+stats.isSocket()+:: ...
|
+stats.isSocket()+:: ...
|
||||||
|
|
||||||
|
=== +fs.FileReadStream+
|
||||||
|
|
||||||
|
[cols="1,2,10",options="header"]
|
||||||
|
|=========================================================
|
||||||
|
|Event | Parameters | Notes
|
||||||
|
|
||||||
|
|+"open"+ | +fd+ | The file descriptor was opened.
|
||||||
|
|+"data"+ | +chunk+ | A chunk of data was read.
|
||||||
|
|+"error"+ | +err+ | An error occured. This stops the stream.
|
||||||
|
|+"end"+ | | The end of the file was reached.
|
||||||
|
|+"close"+ | | The file descriptor was closed.
|
||||||
|
|=========================================================
|
||||||
|
|
||||||
|
+fs.createReadStream(path, [options]);+ ::
|
||||||
|
Returns a new FileReadStream object.
|
||||||
|
+
|
||||||
|
+options+ is an object with the following defaults:
|
||||||
|
+
|
||||||
|
----------------------------------------
|
||||||
|
{ "flags": "r"
|
||||||
|
, "encoding": "binary"
|
||||||
|
, "mode": 0666
|
||||||
|
, "bufferSize": 4 * 1024
|
||||||
|
}
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
+readStream.readable+ ::
|
||||||
|
A boolean that is +true+ by default, but turns +false+ after an +"error"+
|
||||||
|
occured, the stream came to an "end", or +forceClose()+ was called.
|
||||||
|
|
||||||
|
+readStream.pause()+ ::
|
||||||
|
Stops the stream from reading further data. No +"data"+ event will be fired
|
||||||
|
until the stream is resumed.
|
||||||
|
|
||||||
|
+readStream.resume()+ ::
|
||||||
|
Resumes the stream. Together with +pause()+ this useful to throttle reading.
|
||||||
|
|
||||||
|
+readStream.forceClose()+ ::
|
||||||
|
Allows to close the stream before the +"end"+ is reached. No more events other
|
||||||
|
than +"close"+ will be fired after this method has been called.
|
||||||
|
|
||||||
|
=== +fs.FileWriteStream+
|
||||||
|
|
||||||
|
[cols="1,2,10",options="header"]
|
||||||
|
|=========================================================
|
||||||
|
|Event | Parameters | Notes
|
||||||
|
|
||||||
|
|+"open"+ | +fd+ | The file descriptor was opened.
|
||||||
|
|+"drain"+ | | No more data needs to be written.
|
||||||
|
|+"error"+ | +err+ | An error occured. This stops the stream.
|
||||||
|
|+"close"+ | | The file descriptor was closed.
|
||||||
|
|=========================================================
|
||||||
|
|
||||||
|
+fs.createWriteStream(path, [options]);+ ::
|
||||||
|
Returns a new FileWriteStream object.
|
||||||
|
+
|
||||||
|
+options+ is an object with the following defaults:
|
||||||
|
+
|
||||||
|
----------------------------------------
|
||||||
|
{ "flags": "r"
|
||||||
|
, "encoding": "binary"
|
||||||
|
, "mode": 0666
|
||||||
|
}
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
+writeStream.writeable+ ::
|
||||||
|
A boolean that is +true+ by default, but turns +false+ after an +"error"+
|
||||||
|
occured or +close()+ / +forceClose()+ was called.
|
||||||
|
|
||||||
|
+writeStream.write(data)+ ::
|
||||||
|
Returns +true+ if the data was flushed to the kernel, and +false+ if it was
|
||||||
|
queued up for being written later. A +"drain"+ will fire after all queued data
|
||||||
|
has been written.
|
||||||
|
|
||||||
|
+writeStream.close()+ ::
|
||||||
|
Closes the stream right after all queued +write()+ calls have finished.
|
||||||
|
|
||||||
|
+writeStream.forceClose()+ ::
|
||||||
|
Allows to close the stream regardless of its current state.
|
||||||
|
|
||||||
== HTTP
|
== HTTP
|
||||||
|
|
||||||
To use the HTTP server and client one must +require("http")+.
|
To use the HTTP server and client one must +require("http")+.
|
||||||
|
204
lib/fs.js
204
lib/fs.js
@ -1,3 +1,7 @@
|
|||||||
|
var
|
||||||
|
sys = require('sys'),
|
||||||
|
events = require('events');
|
||||||
|
|
||||||
exports.Stats = process.Stats;
|
exports.Stats = process.Stats;
|
||||||
|
|
||||||
process.Stats.prototype._checkModeProperty = function (property) {
|
process.Stats.prototype._checkModeProperty = function (property) {
|
||||||
@ -421,3 +425,203 @@ exports.realpath = function (path, callback) {
|
|||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.createReadStream = function(path, options) {
|
||||||
|
return new FileReadStream(path, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
var FileReadStream = exports.FileReadStream = function(path, options) {
|
||||||
|
events.EventEmitter.call(this);
|
||||||
|
|
||||||
|
this.path = path;
|
||||||
|
this.fd = null;
|
||||||
|
this.readable = true;
|
||||||
|
this.paused = false;
|
||||||
|
|
||||||
|
this.flags = 'r';
|
||||||
|
this.encoding = 'binary';
|
||||||
|
this.mode = 0666;
|
||||||
|
this.bufferSize = 4 * 1024;
|
||||||
|
|
||||||
|
process.mixin(this, options || {});
|
||||||
|
|
||||||
|
var
|
||||||
|
self = this,
|
||||||
|
buffer = null;
|
||||||
|
|
||||||
|
function read() {
|
||||||
|
if (!self.readable || self.paused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.read(self.fd, self.bufferSize, undefined, self.encoding, function(err, data, bytesRead) {
|
||||||
|
if (err) {
|
||||||
|
self.emit('error', err);
|
||||||
|
self.readable = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytesRead === 0) {
|
||||||
|
self.emit('end');
|
||||||
|
self.forceClose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not emit events if the stream is paused
|
||||||
|
if (self.paused) {
|
||||||
|
buffer = data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not emit events anymore after we declared the stream unreadable
|
||||||
|
if (!self.readable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.emit('data', data);
|
||||||
|
read();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.open(this.path, this.flags, this.mode, function(err, fd) {
|
||||||
|
if (err) {
|
||||||
|
self.emit('error', err);
|
||||||
|
self.readable = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.fd = fd;
|
||||||
|
self.emit('open', fd);
|
||||||
|
read();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.forceClose = function() {
|
||||||
|
this.readable = false;
|
||||||
|
fs.close(this.fd, function(err) {
|
||||||
|
if (err) {
|
||||||
|
self.emit('error', err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.emit('close');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.pause = function() {
|
||||||
|
this.paused = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.resume = function() {
|
||||||
|
this.paused = false;
|
||||||
|
|
||||||
|
if (buffer !== null) {
|
||||||
|
self.emit('data', buffer);
|
||||||
|
buffer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
read();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
sys.inherits(FileReadStream, events.EventEmitter);
|
||||||
|
|
||||||
|
exports.createWriteStream = function(path, options) {
|
||||||
|
return new FileWriteStream(path, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
var FileWriteStream = exports.FileWriteStream = function(path, options) {
|
||||||
|
events.EventEmitter.call(this);
|
||||||
|
|
||||||
|
this.path = path;
|
||||||
|
this.fd = null;
|
||||||
|
this.writeable = true;
|
||||||
|
|
||||||
|
this.flags = 'w';
|
||||||
|
this.encoding = 'binary';
|
||||||
|
this.mode = 0666;
|
||||||
|
|
||||||
|
process.mixin(this, options || {});
|
||||||
|
|
||||||
|
var
|
||||||
|
self = this,
|
||||||
|
queue = [],
|
||||||
|
busy = false;
|
||||||
|
|
||||||
|
queue.push([fs.open, this.path, this.flags, this.mode]);
|
||||||
|
|
||||||
|
function flush() {
|
||||||
|
if (busy) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var args = queue.shift();
|
||||||
|
if (!args) {
|
||||||
|
return self.emit('drain');
|
||||||
|
}
|
||||||
|
|
||||||
|
busy = true;
|
||||||
|
|
||||||
|
var method = args.shift();
|
||||||
|
|
||||||
|
args.push(function(err) {
|
||||||
|
busy = false;
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
self.writeable = false;
|
||||||
|
self.emit('error', err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// save reference for file pointer
|
||||||
|
if (method === fs.open) {
|
||||||
|
self.fd = arguments[1];
|
||||||
|
self.emit('open', self.fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop flushing after close
|
||||||
|
if (method === fs.close) {
|
||||||
|
self.emit('close');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
flush();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Inject the file pointer
|
||||||
|
if (method !== fs.open) {
|
||||||
|
args.unshift(self.fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
method.apply(null, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.write = function(data) {
|
||||||
|
if (!this.writeable) {
|
||||||
|
throw new Error('stream not writeable');
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.push([fs.write, data, undefined, this.encoding]);
|
||||||
|
flush();
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.close = function() {
|
||||||
|
this.writeable = false;
|
||||||
|
queue.push([fs.close,]);
|
||||||
|
flush();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.forceClose = function() {
|
||||||
|
this.writeable = false;
|
||||||
|
fs.close(self.fd, function(err) {
|
||||||
|
if (err) {
|
||||||
|
self.emit('error', err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.emit('close');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
flush();
|
||||||
|
};
|
||||||
|
sys.inherits(FileWriteStream, events.EventEmitter);
|
54
test/simple/test-file-read-stream.js
Normal file
54
test/simple/test-file-read-stream.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
process.mixin(require('../common'));
|
||||||
|
|
||||||
|
var
|
||||||
|
fn = path.join(fixturesDir, 'multipart.js'),
|
||||||
|
file = fs.createReadStream(fn),
|
||||||
|
|
||||||
|
callbacks = {
|
||||||
|
open: -1,
|
||||||
|
end: -1,
|
||||||
|
close: -1
|
||||||
|
},
|
||||||
|
|
||||||
|
paused = false,
|
||||||
|
|
||||||
|
fileContent = '';
|
||||||
|
|
||||||
|
file
|
||||||
|
.addListener('open', function(fd) {
|
||||||
|
callbacks.open++;
|
||||||
|
assert.equal('number', typeof fd);
|
||||||
|
assert.ok(file.readable);
|
||||||
|
})
|
||||||
|
.addListener('error', function(err) {
|
||||||
|
throw err;
|
||||||
|
})
|
||||||
|
.addListener('data', function(data) {
|
||||||
|
assert.ok(!paused);
|
||||||
|
fileContent += data;
|
||||||
|
|
||||||
|
paused = true;
|
||||||
|
file.pause();
|
||||||
|
assert.ok(file.paused);
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
paused = false;
|
||||||
|
file.resume();
|
||||||
|
assert.ok(!file.paused);
|
||||||
|
}, 10);
|
||||||
|
})
|
||||||
|
.addListener('end', function(chunk) {
|
||||||
|
callbacks.end++;
|
||||||
|
})
|
||||||
|
.addListener('close', function() {
|
||||||
|
callbacks.close++;
|
||||||
|
assert.ok(!file.readable);
|
||||||
|
|
||||||
|
assert.equal(fs.readFileSync(fn), fileContent);
|
||||||
|
});
|
||||||
|
|
||||||
|
process.addListener('exit', function() {
|
||||||
|
for (var k in callbacks) {
|
||||||
|
assert.equal(0, callbacks[k], k+' count off by '+callbacks[k]);
|
||||||
|
}
|
||||||
|
});
|
50
test/simple/test-file-write-stream.js
Normal file
50
test/simple/test-file-write-stream.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
process.mixin(require('../common'));
|
||||||
|
|
||||||
|
var
|
||||||
|
fn = path.join(fixturesDir, "write.txt"),
|
||||||
|
file = fs.createWriteStream(fn),
|
||||||
|
|
||||||
|
EXPECTED = '0123456789',
|
||||||
|
|
||||||
|
callbacks = {
|
||||||
|
open: -1,
|
||||||
|
drain: -2,
|
||||||
|
close: -1
|
||||||
|
};
|
||||||
|
|
||||||
|
file
|
||||||
|
.addListener('open', function(fd) {
|
||||||
|
callbacks.open++;
|
||||||
|
assert.equal('number', typeof fd);
|
||||||
|
})
|
||||||
|
.addListener('error', function(err) {
|
||||||
|
throw err;
|
||||||
|
})
|
||||||
|
.addListener('drain', function() {
|
||||||
|
callbacks.drain++;
|
||||||
|
if (callbacks.drain == -1) {
|
||||||
|
assert.equal(EXPECTED, fs.readFileSync(fn));
|
||||||
|
file.write(EXPECTED);
|
||||||
|
} else if (callbacks.drain == 0) {
|
||||||
|
assert.equal(EXPECTED+EXPECTED, fs.readFileSync(fn));
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.addListener('close', function() {
|
||||||
|
callbacks.close++;
|
||||||
|
assert.throws(function() {
|
||||||
|
file.write('should not work anymore');
|
||||||
|
});
|
||||||
|
|
||||||
|
fs.unlinkSync(fn);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (var i = 0; i < 10; i++) {
|
||||||
|
assert.strictEqual(false, file.write(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
process.addListener('exit', function() {
|
||||||
|
for (var k in callbacks) {
|
||||||
|
assert.equal(0, callbacks[k], k+' count off by '+callbacks[k]);
|
||||||
|
}
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user