Skip to content

Commit ab0107f

Browse files
committed
fs: add FileHandle.prototype.readableWebStream()
Adds an experimental `readableWebStream()` method to `FileHandle` that returns a web `ReadableStream` Signed-off-by: James M Snell <[email protected]>
1 parent a6407eb commit ab0107f

File tree

3 files changed

+108
-0
lines changed

3 files changed

+108
-0
lines changed

doc/api/fs.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,44 @@ Reads data from the file and stores that in the given buffer.
302302
If the file is not modified concurrently, the end-of-file is reached when the
303303
number of bytes read is zero.
304304
305+
#### `filehandle.readableWebStream()`
306+
<!-- YAML
307+
added: REPLACEME
308+
-->
309+
310+
> Stability: 1 - Experimental
311+
312+
* Returns: {ReadableStream}
313+
314+
Returns a `ReadableStream` that may be used to read the files data.
315+
316+
An error will be thrown if this method is called more than once or is called
317+
after the `FileHandle` is closed or closing.
318+
319+
```mjs
320+
import {
321+
open,
322+
} from 'node:fs/promises';
323+
324+
const file = await open('./some/file/to/read');
325+
326+
for await (const chunk of file.readableWebStream())
327+
console.log(chunk);
328+
```
329+
330+
```cjs
331+
const {
332+
open,
333+
} = require('fs/promises');
334+
335+
(async () => {
336+
const file = await open('./some/file/to/read');
337+
338+
for await (const chunk of file.readableWebStream())
339+
console.log(chunk);
340+
})();
341+
```
342+
305343
#### `filehandle.readFile(options)`
306344
<!-- YAML
307345
added: v10.0.0

lib/internal/fs/promises.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const {
3030
codes: {
3131
ERR_FS_FILE_TOO_LARGE,
3232
ERR_INVALID_ARG_VALUE,
33+
ERR_INVALID_STATE,
3334
ERR_METHOD_NOT_IMPLEMENTED,
3435
},
3536
AbortError,
@@ -90,12 +91,17 @@ const kCloseResolve = Symbol('kCloseResolve');
9091
const kCloseReject = Symbol('kCloseReject');
9192
const kRef = Symbol('kRef');
9293
const kUnref = Symbol('kUnref');
94+
const kLocked = Symbol('kLocked');
9395

9496
const { kUsePromises } = binding;
9597
const {
9698
JSTransferable, kDeserialize, kTransfer, kTransferList
9799
} = require('internal/worker/js_transferable');
98100

101+
const {
102+
newReadableStreamFromStreamBase,
103+
} = require('internal/webstreams/adapters');
104+
99105
const getDirectoryEntriesPromise = promisify(getDirents);
100106
const validateRmOptionsPromise = promisify(validateRmOptions);
101107

@@ -209,6 +215,22 @@ class FileHandle extends EventEmitterMixin(JSTransferable) {
209215
return this[kClosePromise];
210216
}
211217

218+
/**
219+
* @typedef {import('../webstreams/readablestream').ReadableStream
220+
* } ReadableStream
221+
* @returns {ReadableStream}
222+
*/
223+
readableWebStream() {
224+
if (this[kFd] === -1)
225+
throw new ERR_INVALID_STATE('The FileHandle is closed');
226+
if (this[kClosePromise])
227+
throw new ERR_INVALID_STATE('The FileHandle is closing');
228+
if (this[kLocked])
229+
throw new ERR_INVALID_STATE('The FileHandle is locked');
230+
this[kLocked] = true;
231+
return newReadableStreamFromStreamBase(this[kHandle]);
232+
}
233+
212234
[kTransfer]() {
213235
if (this[kClosePromise] || this[kRefs] > 1) {
214236
throw lazyDOMException('Cannot transfer FileHandle while in use',
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('assert');
5+
6+
const {
7+
readFileSync,
8+
} = require('fs');
9+
10+
const {
11+
open,
12+
} = require('fs/promises');
13+
14+
const check = readFileSync(__filename, { encoding: 'utf8' });
15+
16+
(async () => {
17+
const dec = new TextDecoder();
18+
const file = await open(__filename);
19+
let data = '';
20+
for await (const chunk of file.readableWebStream())
21+
data += dec.decode(chunk);
22+
23+
assert.strictEqual(check, data);
24+
25+
assert.throws(() => file.readableWebStream(), {
26+
code: 'ERR_INVALID_STATE',
27+
});
28+
29+
await file.close();
30+
})().then(common.mustCall());
31+
32+
(async () => {
33+
const file = await open(__filename);
34+
await file.close();
35+
36+
assert.throws(() => file.readableWebStream(), {
37+
code: 'ERR_INVALID_STATE',
38+
});
39+
})().then(common.mustCall());
40+
41+
(async () => {
42+
const file = await open(__filename);
43+
file.close();
44+
45+
assert.throws(() => file.readableWebStream(), {
46+
code: 'ERR_INVALID_STATE',
47+
});
48+
})().then(common.mustCall());

0 commit comments

Comments
 (0)