From dfb14cd7ef07fb6f950559158a77591114f77259 Mon Sep 17 00:00:00 2001 From: zhw <116932167+coderz-w@users.noreply.github.com> Date: Thu, 11 Jul 2024 18:04:21 +0800 Subject: [PATCH] fix:delay folder upload until all children files are parsed when drag (#561) * fix:delay folder upload until all children files are parsed when drag * fix: isAcceptable case * feat: use queue * feat: readEntries add errorCallback * fix: improve code * feat:improve code * fix:ci * feat: improve code --- src/traverseFileTree.ts | 59 +++++++++++++++++++++-------------------- tests/uploader.spec.tsx | 49 +++++++++++++++++++++++++++++++++- 2 files changed, 78 insertions(+), 30 deletions(-) diff --git a/src/traverseFileTree.ts b/src/traverseFileTree.ts index 06665d3..7b74c0b 100644 --- a/src/traverseFileTree.ts +++ b/src/traverseFileTree.ts @@ -10,30 +10,28 @@ interface InternalDataTransferItem extends DataTransferItem { path: string; } -function loopFiles(item: InternalDataTransferItem, callback) { - const dirReader = item.createReader(); - let fileList = []; +const traverseFileTree = (files: InternalDataTransferItem[], callback, isAccepted) => { + const flattenFileList = []; + const progressFileList = []; + files.forEach(file => progressFileList.push(file.webkitGetAsEntry() as any)); + function loopFiles(item: InternalDataTransferItem) { + const dirReader = item.createReader(); - function sequence() { - dirReader.readEntries((entries: InternalDataTransferItem[]) => { - const entryList = Array.prototype.slice.apply(entries); - fileList = fileList.concat(entryList); + function sequence() { + dirReader.readEntries((entries: InternalDataTransferItem[]) => { + const entryList = Array.prototype.slice.apply(entries); - // Check if all the file has been viewed - const isFinished = !entryList.length; + progressFileList.push(...entryList); + // Check if all the file has been viewed + const isFinished = !entryList.length; + if (!isFinished) { + sequence(); + } + }); + } - if (isFinished) { - callback(fileList); - } else { - sequence(); - } - }); + sequence(); } - - sequence(); -} - -const traverseFileTree = (files: InternalDataTransferItem[], callback, isAccepted) => { // eslint-disable-next-line @typescript-eslint/naming-convention const _traverseFileTree = (item: InternalDataTransferItem, path?: string) => { if (!item) { @@ -59,20 +57,23 @@ const traverseFileTree = (files: InternalDataTransferItem[], callback, isAccepte }, }); } - callback([file]); + flattenFileList.push(file); } }); } else if (item.isDirectory) { - loopFiles(item, (entries: InternalDataTransferItem[]) => { - entries.forEach(entryItem => { - _traverseFileTree(entryItem, `${path}${item.name}/`); - }); - }); + loopFiles(item); } }; - files.forEach(file => { - _traverseFileTree(file.webkitGetAsEntry() as any); - }); + + function walkFiles() { + let wipIndex = 0; + while (wipIndex < progressFileList.length) { + _traverseFileTree(progressFileList[wipIndex]); + wipIndex++; + } + callback(flattenFileList); + } + walkFiles(); }; export default traverseFileTree; diff --git a/tests/uploader.spec.tsx b/tests/uploader.spec.tsx index b7e060c..ba3e47d 100644 --- a/tests/uploader.spec.tsx +++ b/tests/uploader.spec.tsx @@ -25,7 +25,7 @@ const makeFileSystemEntry = item => { return { readEntries(handle) { if (!first) { - return []; + return handle([]); } first = false; @@ -373,7 +373,54 @@ describe('uploader', () => { beforeEach(() => { uploader = render(); }); + it('beforeUpload should run after all children files are parsed', done => { + const props = { action: '/test', directory: true, accept: '.png' }; + const mockBeforeUpload = jest.fn(); + const beforeUpload = (file, fileList) => { + console.log('beforeUpload', file, fileList); + mockBeforeUpload(file, fileList); + }; + const Test = () => { + return ; + }; + const { container } = render(); + const files = { + name: 'foo', + children: [ + { + name: 'bar', + children: [ + { + name: '1.png', + }, + { + name: '2.png', + }, + { + name: 'rc', + children: [ + { + name: '5.webp', + }, + { + name: '4.webp', + }, + ], + }, + ], + }, + ], + }; + const input = container.querySelector('input')!; + fireEvent.drop(input, { dataTransfer: { items: [makeDataTransferItem(files)] } }); + setTimeout(() => { + expect(mockBeforeUpload.mock.calls.length).toBe(2); + expect(mockBeforeUpload.mock.calls[0][1].length).toBe(2); + expect(mockBeforeUpload.mock.calls[1][1].length).toBe(2); + done(); + }, 100); + }); it('unaccepted type files to upload will not trigger onStart', done => { const input = uploader.container.querySelector('input')!; const files = {