From 94c3722c3470de8db82ae3afb9f55dfbc4bd3c58 Mon Sep 17 00:00:00 2001 From: achingbrain Date: Thu, 17 Nov 2022 17:01:37 +0000 Subject: [PATCH] fix: mfs blob import for files larger than 262144b Turn the `Blob`/`File` object into a `ReadableStream` then turn that into an `AsyncIterator` the same as the other input types. Fixes #3601 Fixes #3576 --- .../interface-ipfs-core/src/files/stat.js | 22 ++++++++ .../files/utils/to-async-iterator.js | 51 +------------------ 2 files changed, 24 insertions(+), 49 deletions(-) diff --git a/packages/interface-ipfs-core/src/files/stat.js b/packages/interface-ipfs-core/src/files/stat.js index 04e949764f..3b63389c4a 100644 --- a/packages/interface-ipfs-core/src/files/stat.js +++ b/packages/interface-ipfs-core/src/files/stat.js @@ -11,6 +11,7 @@ import { identity } from 'multiformats/hashes/identity' import { randomBytes } from 'iso-random-stream' import isShardAtPath from '../utils/is-shard-at-path.js' import * as raw from 'multiformats/codecs/raw' +import { isBrowser } from 'wherearewe' /** * @typedef {import('ipfsd-ctl').Factory} Factory @@ -103,6 +104,27 @@ export function testStat (factory, options) { }) }) + it('should stat a large browser File', async function () { + if (!isBrowser) { + this.skip() + } + + const filePath = `/stat-${Math.random()}/large-file-${Math.random()}.txt` + const blob = new Blob([largeFile]) + + await ipfs.files.write(filePath, blob, { + create: true, + parents: true + }) + + await expect(ipfs.files.stat(filePath)).to.eventually.include({ + size: largeFile.length, + cumulativeSize: 490800, + blocks: 2, + type: 'file' + }) + }) + it('stats a raw node', async () => { const filePath = `/stat-${Math.random()}/large-file-${Math.random()}.txt` diff --git a/packages/ipfs-core/src/components/files/utils/to-async-iterator.js b/packages/ipfs-core/src/components/files/utils/to-async-iterator.js index 06f7a9ee5c..219b959a19 100644 --- a/packages/ipfs-core/src/components/files/utils/to-async-iterator.js +++ b/packages/ipfs-core/src/components/files/utils/to-async-iterator.js @@ -1,9 +1,7 @@ import errCode from 'err-code' import { logger } from '@libp2p/logger' -import { - MFS_MAX_CHUNK_SIZE -} from '../../../utils.js' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' +import browserStreamToIt from 'browser-readablestream-to-it' const log = logger('ipfs:mfs:utils:to-async-iterator') @@ -44,52 +42,7 @@ export function toAsyncIterator (content) { if (global.Blob && content instanceof global.Blob) { // HTML5 Blob objects (including Files) log('Content was an HTML5 Blob') - - let index = 0 - - const iterator = { - next: () => { - if (index > content.size) { - return { - done: true - } - } - - return new Promise((resolve, reject) => { - const chunk = content.slice(index, MFS_MAX_CHUNK_SIZE) - index += MFS_MAX_CHUNK_SIZE - - const reader = new global.FileReader() - - /** - * @param {{ error?: Error }} ev - */ - const handleLoad = (ev) => { - // @ts-expect-error No overload matches this call. - reader.removeEventListener('loadend', handleLoad, false) - - if (ev.error) { - return reject(ev.error) - } - - resolve({ - done: false, - value: new Uint8Array(/** @type {ArrayBuffer} */(reader.result)) - }) - } - - // @ts-expect-error No overload matches this call. - reader.addEventListener('loadend', handleLoad) - reader.readAsArrayBuffer(chunk) - }) - } - } - - return { - [Symbol.asyncIterator]: () => { - return iterator - } - } + return browserStreamToIt(content.stream()) } throw errCode(new Error(`Don't know how to convert ${content} into an async iterator`), 'ERR_INVALID_PARAMS')