fix: should be able to upload multiple large files (#23360)

Fixes https://github.com/microsoft/playwright/issues/23258
This commit is contained in:
Max Schmitt 2023-05-30 18:41:09 +02:00 committed by GitHub
parent 1f7223eb21
commit 2710fd8d6f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 54 additions and 4 deletions

View file

@ -266,13 +266,13 @@ export async function convertInputFiles(files: string | FilePayload | string[] |
const items: (string | FilePayload)[] = Array.isArray(files) ? files.slice() : [files];
const sizeLimit = 50 * 1024 * 1024;
const hasLargeBuffer = items.find(item => typeof item === 'object' && item.buffer && item.buffer.byteLength > sizeLimit);
if (hasLargeBuffer)
const totalBufferSizeExceedsLimit = items.reduce((size, item) => size + ((typeof item === 'object' && item.buffer) ? item.buffer.byteLength : 0), 0) > sizeLimit;
if (totalBufferSizeExceedsLimit)
throw new Error('Cannot set buffer larger than 50Mb, please write it to a file and pass its path instead.');
const stats = await Promise.all(items.filter(isString).map(item => fs.promises.stat(item as string)));
const hasLargeFile = !!stats.find(s => s.size > sizeLimit);
if (hasLargeFile) {
const totalFileSizeExceedsLimit = stats.reduce((acc, stat) => acc + stat.size, 0) > sizeLimit;
if (totalFileSizeExceedsLimit) {
if (context._connection.isRemote()) {
const streams: channels.WritableStreamChannel[] = await Promise.all(items.map(async item => {
assert(isString(item));

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>File upload test</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" multiple name="file1">
<input type="submit">
</form>
</body>
</html>

View file

@ -85,6 +85,44 @@ it('should upload large file', async ({ page, server, browserName, isMac, isAndr
await Promise.all([uploadFile, file1.filepath].map(fs.promises.unlink));
});
it('should upload multiple large files', async ({ page, server, browserName, isMac, isAndroid }, testInfo) => {
it.skip(browserName === 'webkit' && isMac && parseInt(os.release(), 10) < 20, 'WebKit for macOS 10.15 is frozen and does not have corresponding protocol features.');
it.skip(isAndroid);
it.slow();
const filesCount = 10;
await page.goto(server.PREFIX + '/input/fileupload-multi.html');
const uploadFile = testInfo.outputPath('50MB_1.zip');
const str = 'A'.repeat(1024);
const stream = fs.createWriteStream(uploadFile);
// 49 is close to the actual limit
for (let i = 0; i < 49 * 1024; i++) {
await new Promise<void>((fulfill, reject) => {
stream.write(str, err => {
if (err)
reject(err);
else
fulfill();
});
});
}
await new Promise(f => stream.end(f));
const input = page.locator('input[type="file"]');
const uploadFiles = [uploadFile];
for (let i = 2; i <= filesCount; i++) {
const dstFile = testInfo.outputPath(`50MB_${i}.zip`);
fs.copyFileSync(uploadFile, dstFile);
uploadFiles.push(dstFile);
}
const fileChooserPromise = page.waitForEvent('filechooser');
await input.click();
const fileChooser = await fileChooserPromise;
await fileChooser.setFiles(uploadFiles);
const filesLen = await page.evaluate('document.getElementsByTagName("input")[0].files.length');
expect(fileChooser.isMultiple()).toBe(true);
expect(filesLen).toEqual(filesCount);
await Promise.all(uploadFiles.map(path => fs.promises.unlink(path)));
});
it('should upload large file with relative path', async ({ page, server, browserName, isMac, isAndroid }, testInfo) => {
it.skip(browserName === 'webkit' && isMac && parseInt(os.release(), 10) < 20, 'WebKit for macOS 10.15 is frozen and does not have corresponding protocol features.');
it.skip(isAndroid);