diff --git a/.changeset/light-snails-leave.md b/.changeset/light-snails-leave.md new file mode 100644 index 0000000..f34da86 --- /dev/null +++ b/.changeset/light-snails-leave.md @@ -0,0 +1,5 @@ +--- +"@remix-run/web-fetch": patch +--- + +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. diff --git a/packages/fetch/src/utils/form-data.js b/packages/fetch/src/utils/form-data.js index d582a15..d499f79 100644 --- a/packages/fetch/src/utils/form-data.js +++ b/packages/fetch/src/utils/form-data.js @@ -104,8 +104,10 @@ export const toFormData = async (source) => { const form = new FormData() const parts = iterateMultipart(body, boundary) for await (const { name, data, filename, contentType } of parts) { - if (filename) { + if (typeof filename === 'string') { form.append(name, new File([data], filename, { type: contentType })) + } else if (typeof filename !== 'undefined') { + form.append(name, new File([], '', { type: contentType })) } else { form.append(name, new TextDecoder().decode(data), filename) } diff --git a/packages/fetch/test/request.js b/packages/fetch/test/request.js index d35e42c..bf85b76 100644 --- a/packages/fetch/test/request.js +++ b/packages/fetch/test/request.js @@ -439,4 +439,52 @@ describe('Request', () => { expect(file.lastModified).to.be.a('number'); }); }); + + it('should decode empty file inputs into File instances (web FormData)', async () => { + const ogFormData = new WebFormData(); + ogFormData.append('a', 1); + // This is what happens when you construct the form data set with an empty file input: + // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#constructing-the-form-data-set + ogFormData.append('file', new File([], '', { type: 'application/octet-stream' })); + const request = new Request(base, { + method: 'POST', + body: ogFormData, + }); + const clonedRequest = request.clone(); + return clonedRequest.formData().then(async clonedFormData => { + expect(clonedFormData.get('a')).to.equal("1"); + const file = clonedFormData.get('file'); + expect(file.name).to.equal(""); + expect(file.type).to.equal("application/octet-stream"); + expect(file.size).to.equal(0); + }); + }); + + it.skip('should decode empty file inputs into File instances (node FormData)', async () => { + const ogFormData = new FormData(); + ogFormData.append('a', 1); + // This is what happens when you construct the form data set with an empty file input: + // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#constructing-the-form-data-set + ogFormData.append('file', Buffer.from(''), { + // Note: This doesn't work at the moment due to https://github.com/form-data/form-data/issues/412. + // There is a v4 released which has a fix that might handle this but I + // wasn't positive if it had breaking changes that would impact us so we + // can handle an upgrade separately. + filename: '', + contentType: 'application/octet-stream', + }); + const request = new Request(base, { + method: 'POST', + body: ogFormData, + }); + const clonedRequest = request.clone(); + return clonedRequest.formData().then(async clonedFormData => { + expect(clonedFormData.get('a')).to.equal("1"); + const file = clonedFormData.get('file'); + expect(file.name).to.equal(""); + expect(file.type).to.equal("application/octet-stream"); + expect(file.size).to.equal(0); + }); + + }); });