From 86e95307ae5af5aedf63a1ca1fc93f2ffd4a49d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Thu, 11 Feb 2021 21:56:21 +0100 Subject: [PATCH 1/5] wrab blob stream into a node stream --- src/body.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/body.js b/src/body.js index f1233034d..bb65d27e9 100644 --- a/src/body.js +++ b/src/body.js @@ -177,7 +177,7 @@ async function consumeBody(data) { // Body is blob if (isBlob(body)) { - body = body.stream(); + body = Stream.Readable.from(body.stream()); } // Body is buffer @@ -371,7 +371,7 @@ export const writeToStream = (dest, {body}) => { dest.end(); } else if (isBlob(body)) { // Body is Blob - body.stream().pipe(dest); + Stream.Readable.from(body.stream()).pipe(dest); } else if (Buffer.isBuffer(body)) { // Body is buffer dest.write(body); From 36e461944f91a867fa32484a00a8ef9500ddacbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Thu, 11 Feb 2021 23:35:31 +0100 Subject: [PATCH 2/5] now support blob that have no streams --- src/body.js | 5 +++-- src/utils/blob-to-stream.js | 18 ++++++++++++++++++ src/utils/is.js | 7 +++++-- test/response.js | 10 ++++++++++ 4 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 src/utils/blob-to-stream.js diff --git a/src/body.js b/src/body.js index bb65d27e9..6304cfa8b 100644 --- a/src/body.js +++ b/src/body.js @@ -14,6 +14,7 @@ import {FetchError} from './errors/fetch-error.js'; import {FetchBaseError} from './errors/base.js'; import {formDataIterator, getBoundary, getFormDataLength} from './utils/form-data.js'; import {isBlob, isURLSearchParameters, isFormData} from './utils/is.js'; +import {blobToNodeStream} from './utils/blob-to-stream.js'; const INTERNALS = Symbol('Body internals'); @@ -177,7 +178,7 @@ async function consumeBody(data) { // Body is blob if (isBlob(body)) { - body = Stream.Readable.from(body.stream()); + body = blobToNodeStream(body); } // Body is buffer @@ -371,7 +372,7 @@ export const writeToStream = (dest, {body}) => { dest.end(); } else if (isBlob(body)) { // Body is Blob - Stream.Readable.from(body.stream()).pipe(dest); + blobToNodeStream(body).pipe(dest); } else if (Buffer.isBuffer(body)) { // Body is buffer dest.write(body); diff --git a/src/utils/blob-to-stream.js b/src/utils/blob-to-stream.js new file mode 100644 index 000000000..d9681a048 --- /dev/null +++ b/src/utils/blob-to-stream.js @@ -0,0 +1,18 @@ +import {Readable} from 'stream'; + +async function * read(blob) { + let position = 0; + while (position !== blob.size) { + const chunk = blob.slice(position, Math.min(blob.size, position + 524288)); + // eslint-disable-next-line no-await-in-loop + const buffer = await chunk.arrayBuffer(); + position += buffer.byteLength; + yield new Uint8Array(buffer); + } +} + +export function blobToNodeStream(blob) { + return Readable.from(blob.stream ? blob.stream() : read(blob), { + objectMode: false + }); +} diff --git a/src/utils/is.js b/src/utils/is.js index fa8d15922..7f198fbed 100644 --- a/src/utils/is.js +++ b/src/utils/is.js @@ -38,9 +38,12 @@ export const isBlob = object => { typeof object === 'object' && typeof object.arrayBuffer === 'function' && typeof object.type === 'string' && - typeof object.stream === 'function' && + // typeof object.stream === 'function' && typeof object.constructor === 'function' && - /^(Blob|File)$/.test(object[NAME]) + ( + /^(Blob|File)$/.test(object[NAME]) || + /^(Blob|File)$/.test(object.constructor.name) + ) ); }; diff --git a/test/response.js b/test/response.js index f02b67f4d..cd4753974 100644 --- a/test/response.js +++ b/test/response.js @@ -3,6 +3,7 @@ import * as stream from 'stream'; import {TextEncoder} from 'util'; import chai from 'chai'; import Blob from 'fetch-blob'; +import buffer from 'buffer'; import {Response} from '../src/index.js'; import TestServer from './utils/server.js'; @@ -173,6 +174,15 @@ describe('Response', () => { }); }); + if (buffer.Blob) { + it('should support Buffer.Blob as body', () => { + const res = new Response(new buffer.Blob(['a=1'])); + return res.text().then(result => { + expect(result).to.equal('a=1'); + }); + }); + } + it('should support Uint8Array as body', () => { const encoder = new TextEncoder(); const res = new Response(encoder.encode('a=1')); From d4d701881134bf2118ca150c80583f5149122c76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Thu, 11 Feb 2021 23:44:24 +0100 Subject: [PATCH 3/5] make c8 happy, it's a conditional test after all --- src/utils/blob-to-stream.js | 2 ++ src/utils/is.js | 1 + 2 files changed, 3 insertions(+) diff --git a/src/utils/blob-to-stream.js b/src/utils/blob-to-stream.js index d9681a048..8aaf7cc3a 100644 --- a/src/utils/blob-to-stream.js +++ b/src/utils/blob-to-stream.js @@ -1,5 +1,6 @@ import {Readable} from 'stream'; +/* c8 ignore start */ async function * read(blob) { let position = 0; while (position !== blob.size) { @@ -10,6 +11,7 @@ async function * read(blob) { yield new Uint8Array(buffer); } } +/* c8 ignore end */ export function blobToNodeStream(blob) { return Readable.from(blob.stream ? blob.stream() : read(blob), { diff --git a/src/utils/is.js b/src/utils/is.js index 7f198fbed..f690b3c58 100644 --- a/src/utils/is.js +++ b/src/utils/is.js @@ -41,6 +41,7 @@ export const isBlob = object => { // typeof object.stream === 'function' && typeof object.constructor === 'function' && ( + /* c8 ignore next 2 */ /^(Blob|File)$/.test(object[NAME]) || /^(Blob|File)$/.test(object.constructor.name) ) From 380dba56d6b99e979f4d83187d593b430b8d8d6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Sat, 27 Feb 2021 16:51:33 +0100 Subject: [PATCH 4/5] Use a const --- src/utils/blob-to-stream.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/utils/blob-to-stream.js b/src/utils/blob-to-stream.js index 8aaf7cc3a..a38bbebb8 100644 --- a/src/utils/blob-to-stream.js +++ b/src/utils/blob-to-stream.js @@ -1,10 +1,13 @@ import {Readable} from 'stream'; +// 64 KiB (same size chrome slice theirs blob into Uint8array's) +const POOL_SIZE = 65536 + /* c8 ignore start */ async function * read(blob) { let position = 0; while (position !== blob.size) { - const chunk = blob.slice(position, Math.min(blob.size, position + 524288)); + const chunk = blob.slice(position, Math.min(blob.size, position + POOL_SIZE)); // eslint-disable-next-line no-await-in-loop const buffer = await chunk.arrayBuffer(); position += buffer.byteLength; From d02a18bcbb90445945e25110f49befa18010adca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Sat, 27 Feb 2021 16:54:36 +0100 Subject: [PATCH 5/5] Normally never use ; in my own project... --- src/utils/blob-to-stream.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/blob-to-stream.js b/src/utils/blob-to-stream.js index a38bbebb8..824956252 100644 --- a/src/utils/blob-to-stream.js +++ b/src/utils/blob-to-stream.js @@ -1,7 +1,7 @@ import {Readable} from 'stream'; // 64 KiB (same size chrome slice theirs blob into Uint8array's) -const POOL_SIZE = 65536 +const POOL_SIZE = 65536; /* c8 ignore start */ async function * read(blob) {