Skip to content

Commit 389c2b8

Browse files
MichaelDeBoeyedmundhungjacob-ebeybrophdawg11Gozala
authored
fix(fetch): empty filename should not be treated as string (#87)
* fix: empty filename should not be treated as string Co-authored-by: Jacob Ebey <[email protected]> Co-authored-by: Matt Brophy <[email protected]> * chore: fix changeset * chore: add missing WebFormData import * Update packages/fetch/test/request.js --------- Co-authored-by: Edmund Hung <[email protected]> Co-authored-by: Jacob Ebey <[email protected]> Co-authored-by: Matt Brophy <[email protected]> Co-authored-by: Irakli Gozalishvili <[email protected]>
1 parent e156a6e commit 389c2b8

File tree

3 files changed

+56
-1
lines changed

3 files changed

+56
-1
lines changed

.changeset/light-snails-leave.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@web-std/fetch": patch
3+
---
4+
5+
If you create a `FormData` object on the browser with empty file input, a default empty file entry (i.e. `new File([], '')`) would be generated. However, this is currently presented as an empty string instead when you read it on the server. This should fix the discrepancy.

packages/fetch/src/utils/form-data.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,10 @@ export const toFormData = async (source) => {
104104
const form = new FormData()
105105
const parts = iterateMultipart(body, boundary)
106106
for await (const { name, data, filename, contentType } of parts) {
107-
if (filename) {
107+
if (typeof filename === 'string') {
108108
form.append(name, new File([data], filename, { type: contentType }))
109+
} else if (typeof filename !== 'undefined') {
110+
form.append(name, new File([], '', { type: contentType }))
109111
} else {
110112
form.append(name, new TextDecoder().decode(data), filename)
111113
}

packages/fetch/test/request.js

+48
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,54 @@ describe('Request', () => {
384384
expect(result).to.equal('a=1');
385385
});
386386
});
387+
388+
it('should decode empty file inputs into File instances (web FormData)', async () => {
389+
const ogFormData = new WebFormData();
390+
ogFormData.append('a', 1);
391+
// This is what happens when you construct the form data set with an empty file input:
392+
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#constructing-the-form-data-set
393+
ogFormData.append('file', new File([], '', { type: 'application/octet-stream' }));
394+
const request = new Request(base, {
395+
method: 'POST',
396+
body: ogFormData,
397+
});
398+
const clonedRequest = request.clone();
399+
return clonedRequest.formData().then(async clonedFormData => {
400+
expect(clonedFormData.get('a')).to.equal("1");
401+
const file = clonedFormData.get('file');
402+
expect(file.name).to.equal("");
403+
expect(file.type).to.equal("application/octet-stream");
404+
expect(file.size).to.equal(0);
405+
});
406+
});
407+
408+
it.skip('should decode empty file inputs into File instances (node FormData)', async () => {
409+
const ogFormData = new FormData();
410+
ogFormData.append('a', 1);
411+
// This is what happens when you construct the form data set with an empty file input:
412+
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#constructing-the-form-data-set
413+
ogFormData.append('file', Buffer.from(''), {
414+
// Note: This doesn't work at the moment due to https://github.com/form-data/form-data/issues/412.
415+
// There is a v4 released which has a fix that might handle this but I
416+
// wasn't positive if it had breaking changes that would impact us so we
417+
// can handle an upgrade separately.
418+
filename: '',
419+
contentType: 'application/octet-stream',
420+
});
421+
const request = new Request(base, {
422+
method: 'POST',
423+
body: ogFormData,
424+
});
425+
const clonedRequest = request.clone();
426+
return clonedRequest.formData().then(async clonedFormData => {
427+
expect(clonedFormData.get('a')).to.equal("1");
428+
const file = clonedFormData.get('file');
429+
expect(file.name).to.equal("");
430+
expect(file.type).to.equal("application/octet-stream");
431+
expect(file.size).to.equal(0);
432+
});
433+
434+
});
387435

388436
it('should read formData after clone with web FormData body',async () => {
389437
const ogFormData = new WebFormData();

0 commit comments

Comments
 (0)