diff --git a/src/AjaxUploader.tsx b/src/AjaxUploader.tsx index 9477b99..ec2f7f7 100644 --- a/src/AjaxUploader.tsx +++ b/src/AjaxUploader.tsx @@ -170,8 +170,26 @@ class AjaxUploader extends Component { } } - uploadFiles = (files: File[]) => { - const originFiles = [...files] as RcFile[]; + cacheFiles = async (files: File[]) => { + if (files?.length) { + const cachedFiles = await Promise.all( + files.map(async file => { + const buffer = await file.arrayBuffer(); + return new File([buffer], file.name, { + type: file.type, + lastModified: file.lastModified, + }); + }), + ); + + return cachedFiles; + } + + return []; + }; + + uploadFiles = async (files: File[]) => { + const originFiles = (await this.cacheFiles(files)) as RcFile[]; const postFiles = originFiles.map((file: RcFile & { uid?: string }) => { // eslint-disable-next-line no-param-reassign file.uid = getUid(); diff --git a/tests/uploader.spec.tsx b/tests/uploader.spec.tsx index 5b3040c..d13730c 100644 --- a/tests/uploader.spec.tsx +++ b/tests/uploader.spec.tsx @@ -1,5 +1,5 @@ import { resetWarned } from '@rc-component/util/lib/warning'; -import { fireEvent, render } from '@testing-library/react'; +import { act, fireEvent, render } from '@testing-library/react'; import React from 'react'; import sinon from 'sinon'; import { format } from 'util'; @@ -10,6 +10,7 @@ const sleep = (timeout = 500) => new Promise(resolve => setTimeout(resolve, time function Item(name) { this.name = name; this.toString = () => this.name; + this.arrayBuffer = () => Promise.resolve(this.name); } const makeFileSystemEntry = item => { @@ -85,6 +86,18 @@ describe('uploader', () => { let xhr; let errorMock; + beforeAll(() => { + if (!Blob.prototype.arrayBuffer) { + Blob.prototype.arrayBuffer = function () { + return new Promise(resolve => { + const reader = new FileReader(); + reader.onloadend = () => resolve(reader.result as ArrayBuffer); + reader.readAsArrayBuffer(this); + }); + }; + } + }); + beforeEach(() => { xhr = sinon.useFakeXMLHttpRequest(); requests = []; @@ -203,6 +216,9 @@ describe('uploader', () => { toString() { return this.name; }, + arrayBuffer() { + return Promise.resolve(this.name); + }, }, ]; (files as any).item = (i: number) => files[i]; @@ -234,6 +250,9 @@ describe('uploader', () => { toString() { return this.name; }, + arrayBuffer() { + return Promise.resolve(this.name); + }, }, ]; (files as any).item = (i: number) => files[i]; @@ -262,6 +281,9 @@ describe('uploader', () => { toString() { return this.name; }, + arrayBuffer() { + return Promise.resolve(this.name); + }, }, ]; (files as any).item = (i: number) => files[i]; @@ -293,6 +315,9 @@ describe('uploader', () => { toString() { return this.name; }, + arrayBuffer() { + return Promise.resolve(this.name); + }, }, ]; (files as any).item = (i: number) => files[i]; @@ -327,7 +352,6 @@ describe('uploader', () => { handlers.onSuccess = (ret, file) => { try { expect(ret[1]).toEqual(file.name); - expect(file).toHaveProperty('uid'); expect(triggerTimes).toEqual(1); done(); } catch (error) { @@ -346,7 +370,7 @@ describe('uploader', () => { setTimeout(() => { handlers.onSuccess!(['', files[0].name] as any, files[0] as any, null!); - }, 100); + }, 150); }); it('paste to upload', async () => { @@ -359,6 +383,9 @@ describe('uploader', () => { toString() { return this.name; }, + arrayBuffer() { + return Promise.resolve(this.name); + }, }, ]; (files as any).item = (i: number) => files[i]; @@ -388,6 +415,9 @@ describe('uploader', () => { toString() { return this.name; }, + arrayBuffer() { + return Promise.resolve(this.name); + }, }, ]; (files as any).item = (i: number) => files[i]; @@ -419,7 +449,6 @@ describe('uploader', () => { }; handlers.onSuccess = (ret, file) => { expect(ret[1]).toEqual(file.name); - expect(file).toHaveProperty('uid'); expect(triggerTimes).toEqual(1); }; handlers.onError = error => { @@ -604,6 +633,9 @@ describe('uploader', () => { ], }, ], + arrayBuffer() { + return Promise.resolve(this.name); + }, }; const input = container.querySelector('input')!; fireEvent.drop(input, { dataTransfer: { items: [makeDataTransferItem(files)] } }); @@ -629,6 +661,9 @@ describe('uploader', () => { ], }, ], + arrayBuffer() { + return Promise.resolve(this.name); + }, }; fireEvent.drop(input, { dataTransfer: { items: [makeDataTransferItem(files)] } }); @@ -685,6 +720,9 @@ describe('uploader', () => { ], }, ], + arrayBuffer() { + return Promise.resolve(this.name); + }, }; fireEvent.drop(input, { dataTransfer: { items: [makeDataTransferItemAsync(files)] } }); const mockStart = jest.fn(); @@ -734,6 +772,9 @@ describe('uploader', () => { ], }, ], + arrayBuffer() { + return Promise.resolve(this.name); + }, }; const preventDefaultSpy = jest.spyOn(Event.prototype, 'preventDefault'); @@ -758,6 +799,9 @@ describe('uploader', () => { const files = [ { name: 'unaccepted.webp', + arrayBuffer() { + return Promise.resolve(this.name); + }, }, ]; fireEvent.change(input, { target: { files } }); @@ -779,6 +823,9 @@ describe('uploader', () => { const files = [ { name: 'unaccepted.webp', + arrayBuffer() { + return Promise.resolve(this.name); + }, }, ]; fireEvent.change(input, { target: { files } }); @@ -807,6 +854,9 @@ describe('uploader', () => { name: '1.png', }, ], + arrayBuffer() { + return Promise.resolve(this.name); + }, }; fireEvent.mouseEnter(rcUpload); @@ -875,12 +925,21 @@ describe('uploader', () => { [ { name: 'accepted.webp', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, { name: 'accepted.png', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, { name: 'accepted.txt', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, ], 3, @@ -892,9 +951,15 @@ describe('uploader', () => { [ { name: 'unaccepted.webp', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, { name: 'accepted.png', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, ], 1, @@ -906,12 +971,21 @@ describe('uploader', () => { [ { name: 'unaccepted.webp', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, { name: 'accepted.jpg', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, { name: 'accepted.jpeg', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, ], 2, @@ -923,12 +997,21 @@ describe('uploader', () => { [ { name: 'accepted.png', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, { name: 'unaccepted.jpg', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, { name: 'accepted.txt', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, ], 2, @@ -941,10 +1024,16 @@ describe('uploader', () => { { name: 'unaccepted.png', type: 'image/png', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, { name: 'accepted.jpg', type: 'image/jpeg', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, ], 1, @@ -957,14 +1046,23 @@ describe('uploader', () => { { name: 'accepted.png', type: 'image/png', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, { name: 'accepted.jpg', type: 'image/jpeg', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, { name: 'unaccepted.text', type: 'text/plain', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, ], 2, @@ -977,10 +1075,16 @@ describe('uploader', () => { { name: 'accepted.png', type: 'image/png', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, { name: 'accepted.text', type: 'text/plain', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, ], 2, @@ -993,10 +1097,16 @@ describe('uploader', () => { { name: 'accepted.png', type: 'image/png', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, { name: 'accepted.text', type: 'text/plain', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, ], 2, @@ -1009,10 +1119,16 @@ describe('uploader', () => { { name: 'accepted.png', type: 'image/png', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, { name: 'accepted.text', type: 'text/plain', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, ], 2, @@ -1026,10 +1142,16 @@ describe('uploader', () => { { name: 'accepted.png', type: 'image/png', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, { name: 'unaccepted.text', type: 'text/plain', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, ], 2, @@ -1073,14 +1195,40 @@ describe('uploader', () => { testAcceptConfig( 'should work with format only', { format: '.png' }, - [{ name: 'test.png' }, { name: 'test.jpg' }], + [ + { + name: 'test.png', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, + }, + { + name: 'test.jpg', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, + }, + ], 1, ); testAcceptConfig( 'should work with filter: native', { format: '.png', filter: 'native' }, - [{ name: 'test.png' }, { name: 'test.jpg' }], + [ + { + name: 'test.png', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, + }, + { + name: 'test.jpg', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, + }, + ], 2, // native filter bypasses format check ); @@ -1090,7 +1238,20 @@ describe('uploader', () => { format: '.png', filter: (file: any) => file.name.includes('custom'), }, - [{ name: 'custom.jpg' }, { name: 'test.png' }], + [ + { + name: 'custom.jpg', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, + }, + { + name: 'test.png', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, + }, + ], 1, // only custom.jpg passes custom filter ); @@ -1098,8 +1259,20 @@ describe('uploader', () => { 'should work with MIME type format', { format: 'image/*' }, [ - { name: 'test.png', type: 'image/png' }, - { name: 'doc.txt', type: 'text/plain' }, + { + name: 'test.png', + type: 'image/png', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, + }, + { + name: 'doc.txt', + type: 'text/plain', + arrayBuffer: function () { + return Promise.resolve(this.name); + }, + }, ], 1, // only image file passes ); @@ -1137,6 +1310,9 @@ describe('uploader', () => { toString() { return this.name; }, + arrayBuffer() { + return Promise.resolve(this.name); + }, }, ]; @@ -1161,7 +1337,10 @@ describe('uploader', () => { const batchEventFiles = files.map(file => expect.objectContaining({ - file, + file: expect.objectContaining({ + name: file.name, + type: file.type, + }), }), ); @@ -1220,8 +1399,17 @@ describe('uploader', () => { expect(onBatchStart).toHaveBeenCalledWith( files.map(file => expect.objectContaining({ - file, - parsedFile: file.name === 'light.png' ? null : file, + file: expect.objectContaining({ + name: file.name, + type: file.type, + }), + parsedFile: + file.name === 'light.png' + ? null + : expect.objectContaining({ + name: file.name, + type: file.type, + }), }), ), ); @@ -1257,7 +1445,9 @@ describe('uploader', () => { const [action, setAction] = React.useState('light'); async function beforeUpload() { - setAction('bamboo'); + await act(() => { + setAction('bamboo'); + }); await sleep(100); return true; } @@ -1275,6 +1465,9 @@ describe('uploader', () => { toString() { return this.name; }, + arrayBuffer: function () { + return Promise.resolve(this.name); + }, }, ], },