Skip to content

Commit 7f79a5a

Browse files
author
HiroyukiYagihashi
committed
fs: writeFile support AsyncIterable, Iterable & Stream as data argument
Fixes: nodejs#37391
1 parent d345ac9 commit 7f79a5a

File tree

3 files changed

+76
-10
lines changed

3 files changed

+76
-10
lines changed

doc/api/fs.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3866,7 +3866,7 @@ changes:
38663866
-->
38673867
38683868
* `file` {string|Buffer|URL|integer} filename or file descriptor
3869-
* `data` {string|Buffer|TypedArray|DataView|Object}
3869+
* `data` {string|Buffer|TypedArray|DataView|Object|AsyncIterable|Iterable|Stream}
38703870
* `options` {Object|string}
38713871
* `encoding` {string|null} **Default:** `'utf8'`
38723872
* `mode` {integer} **Default:** `0o666`

lib/internal/fs/promises.js

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -663,19 +663,42 @@ async function writeFile(path, data, options) {
663663
options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'w' });
664664
const flag = options.flag || 'w';
665665

666-
if (!isArrayBufferView(data)) {
667-
validateStringAfterArrayBufferView(data, 'data');
668-
data = Buffer.from(data, options.encoding || 'utf8');
666+
if (options.signal?.aborted) {
667+
throw lazyDOMException('The operation was aborted', 'AbortError');
669668
}
670669

671-
if (path instanceof FileHandle)
672-
return writeFileHandle(path, data, options.signal);
670+
if (isStream(data) || isIterable(data)) {
671+
const fd = await open(path, flag, options.mode);
673672

674-
const fd = await open(path, flag, options.mode);
675-
if (options.signal?.aborted) {
676-
throw lazyDOMException('The operation was aborted', 'AbortError');
673+
try {
674+
for await (const buf of data) {
675+
await fd.write(buf);
676+
}
677+
} finally {
678+
await fd.close();
679+
}
680+
} else {
681+
if (!isArrayBufferView(data)) {
682+
validateStringAfterArrayBufferView(data, 'data');
683+
data = Buffer.from(data, options.encoding || 'utf8');
684+
}
685+
686+
if (path instanceof FileHandle) {
687+
return writeFileHandle(path, data, options.signal);
688+
}
689+
690+
const fd = await open(path, flag, options.mode);
691+
return PromisePrototypeFinally(writeFileHandle(fd, data), fd.close);
677692
}
678-
return PromisePrototypeFinally(writeFileHandle(fd, data), fd.close);
693+
}
694+
695+
function isStream(obj) {
696+
return !!(obj && typeof obj.pipe === 'function');
697+
}
698+
699+
function isIterable(obj) {
700+
return !!obj && typeof obj !== 'string' && !obj.buffer
701+
&& (typeof obj[Symbol.iterator] === 'function' || typeof obj[Symbol.asyncIterator] === 'function');
679702
}
680703

681704
async function appendFile(path, data, options) {

test/parallel/test-fs-promises-writefile.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,29 @@ const path = require('path');
77
const tmpdir = require('../common/tmpdir');
88
const assert = require('assert');
99
const tmpDir = tmpdir.path;
10+
const { Readable } = require("stream");
1011

1112
tmpdir.refresh();
1213

1314
const dest = path.resolve(tmpDir, 'tmp.txt');
1415
const otherDest = path.resolve(tmpDir, 'tmp-2.txt');
1516
const buffer = Buffer.from('abc'.repeat(1000));
1617
const buffer2 = Buffer.from('xyz'.repeat(1000));
18+
const stream = Readable.from(["abc".repeat(1000)]);
19+
const iterable = {
20+
[Symbol.iterator]:function* () {
21+
yield "a";
22+
yield "b";
23+
yield "c";
24+
}
25+
};
26+
const asyncIterable = {
27+
async* [Symbol.asyncIterator]() {
28+
yield "a";
29+
yield "b";
30+
yield "c";
31+
}
32+
};
1733

1834
async function doWrite() {
1935
await fsPromises.writeFile(dest, buffer);
@@ -50,9 +66,36 @@ async function doReadWithEncoding() {
5066
assert.deepStrictEqual(data, syncData);
5167
}
5268

69+
async function doWriteStream() {
70+
await fsPromises.writeFile(dest, stream);
71+
let result = "";
72+
for await (const v of stream) result += v;
73+
const data = fs.readFileSync(dest);
74+
assert.deepStrictEqual(data, result);
75+
}
76+
77+
async function doWriteIterable() {
78+
await fsPromises.writeFile(dest, iterable);
79+
let result = "";
80+
for await (const v of iterable) result += v;
81+
const data = fs.readFileSync(dest);
82+
assert.deepStrictEqual(data, result);
83+
}
84+
85+
async function doWriteAsyncIterable() {
86+
await fsPromises.writeFile(dest, asyncIterable);
87+
let result = "";
88+
for await (const v of iterable) result += v;
89+
const data = fs.readFileSync(dest);
90+
assert.deepStrictEqual(data, result);
91+
}
92+
5393
doWrite()
5494
.then(doWriteWithCancel)
5595
.then(doAppend)
5696
.then(doRead)
5797
.then(doReadWithEncoding)
98+
.then(doWriteStream)
99+
.then(doWriteIterable)
100+
.then(doWriteAsyncIterable)
58101
.then(common.mustCall());

0 commit comments

Comments
 (0)