From 9517b0de4a59b9a2334d5a9771a1cf442ba186e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Thu, 10 Mar 2022 11:47:05 +0100 Subject: [PATCH 1/5] fix: avoid reading files when the size is empty --- from.js | 11 +++++++---- test/own-misc-test.js | 6 ++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/from.js b/from.js index 9eaf8bf..a59da3a 100644 --- a/from.js +++ b/from.js @@ -82,13 +82,16 @@ class BlobDataItem { async * stream () { const { mtimeMs } = await stat(this.#path) + const start = this.#start + const end = start + this.size - 1 + if (mtimeMs > this.lastModified) { throw new DOMException('The requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired.', 'NotReadableError') } - yield * createReadStream(this.#path, { - start: this.#start, - end: this.#start + this.size - 1 - }) + + if (end > start) { + yield * createReadStream(this.#path, { start, end }) + } } get [Symbol.toStringTag] () { diff --git a/test/own-misc-test.js b/test/own-misc-test.js index 999837d..abb6fe2 100644 --- a/test/own-misc-test.js +++ b/test/own-misc-test.js @@ -189,6 +189,12 @@ promise_test(async () => { assert_equals(await (await fileFrom('./LICENSE')).text(), license.toString()) }, 'blob part backed up by filesystem slice correctly') +promise_test(async () => { + fs.writeFileSync('temp', '') + await blobFromSync('./temp').text() + fs.unlinkSync('./temp') +}, 'can read empty files') + test(async () => { const blob = blobFromSync('./LICENSE') await new Promise(resolve => setTimeout(resolve, 2000)) From 68f88e20fb863742d3f3f8a9d9c90c03c134bb0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Thu, 10 Mar 2022 12:00:25 +0100 Subject: [PATCH 2/5] also use stat.size for checking if it is modified --- from.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/from.js b/from.js index a59da3a..a9cb632 100644 --- a/from.js +++ b/from.js @@ -65,6 +65,9 @@ class BlobDataItem { this.#start = options.start this.size = options.size this.lastModified = options.lastModified + this.originalSize = options.originalSize === undefined + ? options.size + : options.originalSize } /** @@ -75,17 +78,18 @@ class BlobDataItem { return new BlobDataItem({ path: this.#path, lastModified: this.lastModified, + originalSize: this.originalSize, size: end - start, start: this.#start + start }) } async * stream () { - const { mtimeMs } = await stat(this.#path) + const { mtimeMs, size } = await stat(this.#path) const start = this.#start const end = start + this.size - 1 - if (mtimeMs > this.lastModified) { + if (mtimeMs > this.lastModified || this.originalSize !== size) { throw new DOMException('The requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired.', 'NotReadableError') } From dfe7c96a2cc6a3a6d834949cc5f5d89231e75443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Thu, 10 Mar 2022 12:04:58 +0100 Subject: [PATCH 3/5] bump minor version number --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cfedf63..cc91aa3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fetch-blob", - "version": "3.1.4", + "version": "3.1.5", "description": "Blob & File implementation in Node.js, originally from node-fetch.", "main": "index.js", "type": "module", From 568b3096b40ca2b1b489edde2f648caf84d9c527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Thu, 10 Mar 2022 12:15:53 +0100 Subject: [PATCH 4/5] Don't push empty chunks into the array (creates faster reads) --- index.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 2542ac2..8a3809c 100644 --- a/index.js +++ b/index.js @@ -83,8 +83,12 @@ const _Blob = class Blob { part = encoder.encode(`${element}`) } - this.#size += ArrayBuffer.isView(part) ? part.byteLength : part.size - this.#parts.push(part) + const size = ArrayBuffer.isView(part) ? part.byteLength : part.size + // Avoid pushing empty parts into the array to better GC them + if (size) { + this.#size += size + this.#parts.push(part) + } } this.#endings = `${options.endings === undefined ? 'transparent' : options.endings}` From d4632ad83e399f2b259a2a57b3f01be8bcbe7d1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Thu, 10 Mar 2022 12:19:27 +0100 Subject: [PATCH 5/5] reverted back to avoid making a if check --- from.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/from.js b/from.js index a9cb632..33c4e7b 100644 --- a/from.js +++ b/from.js @@ -86,16 +86,15 @@ class BlobDataItem { async * stream () { const { mtimeMs, size } = await stat(this.#path) - const start = this.#start - const end = start + this.size - 1 if (mtimeMs > this.lastModified || this.originalSize !== size) { throw new DOMException('The requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired.', 'NotReadableError') } - if (end > start) { - yield * createReadStream(this.#path, { start, end }) - } + yield * createReadStream(this.#path, { + start: this.#start, + end: this.#start + this.size - 1 + }) } get [Symbol.toStringTag] () {