Skip to content

Commit 2879395

Browse files
saquibkhanevanlucas
authored andcommitted
fs: add autoClose option to fs.createWriteStream
Add support to fs.createWriteStream and fs.createWriteStream for an autoClose option that behaves similarly to the autoClose option supported by fs.createReadStream and fs.ReadStream. When an instance of fs.createWriteStream created with autoClose === false finishes, it is not destroyed. Its underlying fd is not closed and it is the responsibility of the user to close it. PR-URL: #3679 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Colin Ihrig <[email protected]>
1 parent a8330f7 commit 2879395

File tree

3 files changed

+72
-4
lines changed

3 files changed

+72
-4
lines changed

doc/api/fs.markdown

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,13 +342,20 @@ Returns a new [`WriteStream`][] object. (See [Writable Stream][]).
342342
{ flags: 'w',
343343
defaultEncoding: 'utf8',
344344
fd: null,
345-
mode: 0o666 }
345+
mode: 0o666,
346+
autoClose: true }
346347

347348
`options` may also include a `start` option to allow writing data at
348349
some position past the beginning of the file. Modifying a file rather
349350
than replacing it may require a `flags` mode of `r+` rather than the
350351
default mode `w`. The `defaultEncoding` can be any one of those accepted by [`Buffer`][].
351352

353+
If `autoClose` is set to true (default behavior) on `error` or `end`
354+
the file descriptor will be closed automatically. If `autoClose` is false,
355+
then the file descriptor won't be closed, even if there's an error.
356+
It is your responsiblity to close it and make sure
357+
there's no file descriptor leak.
358+
352359
Like [`ReadStream`][], if `fd` is specified, `WriteStream` will ignore the
353360
`path` argument and will use the specified file descriptor. This means that no
354361
`'open'` event will be emitted. Note that `fd` should be blocking; non-blocking

lib/fs.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1886,6 +1886,7 @@ function WriteStream(path, options) {
18861886
this.mode = options.mode === undefined ? 0o666 : options.mode;
18871887

18881888
this.start = options.start;
1889+
this.autoClose = options.autoClose === undefined ? true : !!options.autoClose;
18891890
this.pos = undefined;
18901891
this.bytesWritten = 0;
18911892

@@ -1907,7 +1908,11 @@ function WriteStream(path, options) {
19071908
this.open();
19081909

19091910
// dispose on finish.
1910-
this.once('finish', this.close);
1911+
this.once('finish', function() {
1912+
if (this.autoClose) {
1913+
this.close();
1914+
}
1915+
});
19111916
}
19121917

19131918
fs.FileWriteStream = fs.WriteStream; // support the legacy name
@@ -1916,7 +1921,9 @@ fs.FileWriteStream = fs.WriteStream; // support the legacy name
19161921
WriteStream.prototype.open = function() {
19171922
fs.open(this.path, this.flags, this.mode, function(er, fd) {
19181923
if (er) {
1919-
this.destroy();
1924+
if (this.autoClose) {
1925+
this.destroy();
1926+
}
19201927
this.emit('error', er);
19211928
return;
19221929
}
@@ -1939,7 +1946,9 @@ WriteStream.prototype._write = function(data, encoding, cb) {
19391946
var self = this;
19401947
fs.write(this.fd, data, 0, data.length, this.pos, function(er, bytes) {
19411948
if (er) {
1942-
self.destroy();
1949+
if (self.autoClose) {
1950+
self.destroy();
1951+
}
19431952
return cb(er);
19441953
}
19451954
self.bytesWritten += bytes;
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const path = require('path');
5+
const fs = require('fs');
6+
7+
const file = path.join(common.tmpDir, 'write-autoclose-opt1.txt');
8+
common.refreshTmpDir();
9+
let stream = fs.createWriteStream(file, {flags: 'w+', autoClose: false});
10+
stream.write('Test1');
11+
stream.end();
12+
stream.on('finish', common.mustCall(function() {
13+
process.nextTick(common.mustCall(function() {
14+
assert.strictEqual(stream.closed, undefined);
15+
assert(stream.fd !== null);
16+
next();
17+
}));
18+
}));
19+
20+
function next() {
21+
// This will tell us if the fd is usable again or not
22+
stream = fs.createWriteStream(null, {fd: stream.fd, start: 0});
23+
stream.write('Test2');
24+
stream.end();
25+
stream.on('finish', common.mustCall(function() {
26+
assert.strictEqual(stream.closed, true);
27+
assert.strictEqual(stream.fd, null);
28+
process.nextTick(common.mustCall(next2));
29+
}));
30+
}
31+
32+
function next2() {
33+
// This will test if after reusing the fd data is written properly
34+
fs.readFile(file, function(err, data) {
35+
assert(!err);
36+
assert.strictEqual(data.toString(), 'Test2');
37+
process.nextTick(common.mustCall(next3));
38+
});
39+
}
40+
41+
function next3() {
42+
// This is to test success scenario where autoClose is true
43+
const stream = fs.createWriteStream(file, {autoClose: true});
44+
stream.write('Test3');
45+
stream.end();
46+
stream.on('finish', common.mustCall(function() {
47+
process.nextTick(common.mustCall(function() {
48+
assert.strictEqual(stream.closed, true);
49+
assert.strictEqual(stream.fd, null);
50+
}));
51+
}));
52+
}

0 commit comments

Comments
 (0)