Skip to content

Commit e88dec3

Browse files
committed
buffer: implement blob.textStream()
Signed-off-by: Matthew Aitken <maitken033380023@gmail.com>
1 parent c27921b commit e88dec3

3 files changed

Lines changed: 54 additions & 1 deletion

File tree

doc/api/buffer.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,18 @@ added:
593593
Returns a promise that fulfills with the contents of the `Blob` decoded as a
594594
UTF-8 string.
595595

596+
### `blob.textStream()`
597+
598+
<!-- YAML
599+
added: REPLACEME
600+
-->
601+
602+
* Returns: {ReadableStream}
603+
604+
Returns a new `ReadableStream` that allows the content of the `Blob` to be read
605+
as a stream of UTF-8 decoded strings. It is equivalent to piping
606+
[`blob.stream()`][] through a [`TextDecoderStream`][] set up with UTF-8.
607+
596608
### `blob.type`
597609

598610
<!-- YAML
@@ -5610,10 +5622,12 @@ introducing security vulnerabilities into an application.
56105622
[`String.prototype.indexOf()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf
56115623
[`String.prototype.lastIndexOf()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf
56125624
[`String.prototype.length`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length
5625+
[`TextDecoderStream`]: webstreams.md#class-textdecoderstream
56135626
[`TypedArray.from()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/from
56145627
[`TypedArray.prototype.set()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/set
56155628
[`TypedArray.prototype.slice()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/slice
56165629
[`TypedArray.prototype.subarray()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/subarray
5630+
[`blob.stream()`]: #blobstream
56175631
[`buf.buffer`]: #bufbuffer
56185632
[`buf.compare()`]: #bufcomparetarget-targetstart-targetend-sourcestart-sourceend
56195633
[`buf.entries()`]: #bufentries

lib/internal/blob.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ const kNotCloneable = Symbol('kNotCloneable');
8585
const disallowedTypeCharacters = /[^\u{0020}-\u{007E}]/u;
8686

8787
let ReadableStream;
88+
let TextDecoderStream;
8889

8990
const enc = new TextEncoder();
9091
let dec;
@@ -100,6 +101,13 @@ function lazyReadableStream(options) {
100101
return new ReadableStream(options);
101102
}
102103

104+
function lazyTextDecoderStream() {
105+
// eslint-disable-next-line no-global-assign
106+
TextDecoderStream ??=
107+
require('internal/webstreams/encoding').TextDecoderStream;
108+
return new TextDecoderStream();
109+
}
110+
103111
const { EOL } = require('internal/constants');
104112

105113
function isBlob(object) {
@@ -331,6 +339,17 @@ class Blob {
331339
throw new ERR_INVALID_THIS('Blob');
332340
return createBlobReaderStream(this[kHandle].getReader());
333341
}
342+
343+
/**
344+
* @returns {ReadableStream}
345+
*/
346+
textStream() {
347+
if (!isBlob(this))
348+
throw new ERR_INVALID_THIS('Blob');
349+
const stream = createBlobReaderStream(this[kHandle].getReader());
350+
const decoder = lazyTextDecoderStream();
351+
return stream.pipeThrough(decoder);
352+
}
334353
}
335354

336355
function TransferableBlob(handle, length, type = '') {
@@ -364,6 +383,7 @@ ObjectDefineProperties(Blob.prototype, {
364383
type: kEnumerableProperty,
365384
slice: kEnumerableProperty,
366385
stream: kEnumerableProperty,
386+
textStream: kEnumerableProperty,
367387
text: kEnumerableProperty,
368388
arrayBuffer: kEnumerableProperty,
369389
bytes: kEnumerableProperty,

test/parallel/test-blob.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
const common = require('../common');
55
const assert = require('assert');
6-
const { Blob } = require('buffer');
6+
const { Blob, File } = require('buffer');
77
const { inspect } = require('util');
88
const { EOL } = require('os');
99
const { kState } = require('internal/webstreams/util');
@@ -524,3 +524,22 @@ assert.throws(() => new Blob({}), {
524524
Blob.prototype.arrayBuffer = arrayBuffer;
525525
}
526526
})().then(common.mustCall());
527+
528+
{
529+
assert.strictEqual(typeof Blob.prototype.textStream, 'function');
530+
assert.strictEqual(typeof File.prototype.textStream, 'function');
531+
assert.strictEqual(File.prototype.textStream, Blob.prototype.textStream);
532+
}
533+
534+
(async () => {
535+
const smiley = Buffer.from('😀', 'utf8');
536+
const blob = new Blob(['hello ', smiley.subarray(0, 2), smiley.subarray(2)]);
537+
const stream = blob.textStream();
538+
assert.ok(stream instanceof ReadableStream);
539+
let result = '';
540+
for await (const chunk of stream) {
541+
assert.strictEqual(typeof chunk, 'string');
542+
result += chunk;
543+
}
544+
assert.strictEqual(result, 'hello 😀');
545+
})().then(common.mustCall());

0 commit comments

Comments
 (0)