Skip to content

Commit 6039a7c

Browse files
saquibkhanjasnell
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 <jasnell@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
1 parent 5a53cba commit 6039a7c

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
@@ -334,13 +334,20 @@ Returns a new [`WriteStream`][] object. (See [Writable Stream][]).
334334
{ flags: 'w',
335335
defaultEncoding: 'utf8',
336336
fd: null,
337-
mode: 0o666 }
337+
mode: 0o666,
338+
autoClose: true }
338339

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

345+
If `autoClose` is set to true (default behavior) on `error` or `end`
346+
the file descriptor will be closed automatically. If `autoClose` is false,
347+
then the file descriptor won't be closed, even if there's an error.
348+
It is your responsiblity to close it and make sure
349+
there's no file descriptor leak.
350+
344351
Like [`ReadStream`][], if `fd` is specified, `WriteStream` will ignore the
345352
`path` argument and will use the specified file descriptor. This means that no
346353
`'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
@@ -1900,6 +1900,7 @@ function WriteStream(path, options) {
19001900
this.mode = options.mode === undefined ? 0o666 : options.mode;
19011901

19021902
this.start = options.start;
1903+
this.autoClose = options.autoClose === undefined ? true : !!options.autoClose;
19031904
this.pos = undefined;
19041905
this.bytesWritten = 0;
19051906

@@ -1921,7 +1922,11 @@ function WriteStream(path, options) {
19211922
this.open();
19221923

19231924
// dispose on finish.
1924-
this.once('finish', this.close);
1925+
this.once('finish', function() {
1926+
if (this.autoClose) {
1927+
this.close();
1928+
}
1929+
});
19251930
}
19261931

19271932
fs.FileWriteStream = fs.WriteStream; // support the legacy name
@@ -1930,7 +1935,9 @@ fs.FileWriteStream = fs.WriteStream; // support the legacy name
19301935
WriteStream.prototype.open = function() {
19311936
fs.open(this.path, this.flags, this.mode, function(er, fd) {
19321937
if (er) {
1933-
this.destroy();
1938+
if (this.autoClose) {
1939+
this.destroy();
1940+
}
19341941
this.emit('error', er);
19351942
return;
19361943
}
@@ -1953,7 +1960,9 @@ WriteStream.prototype._write = function(data, encoding, cb) {
19531960
var self = this;
19541961
fs.write(this.fd, data, 0, data.length, this.pos, function(er, bytes) {
19551962
if (er) {
1956-
self.destroy();
1963+
if (self.autoClose) {
1964+
self.destroy();
1965+
}
19571966
return cb(er);
19581967
}
19591968
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)