多选文件表单提交时File数组被序列化问题求助
问题
开发支持多次添加文件的表单时,选中的文件加入File[]数组后,提交表单时数组被序列化为[object File]格式的字符串,无法正常传递文件数据。
现有代码
<script lang="ts"> let selectedFiles: File[] = []; function handleSubmit(e: any) { errorCode = validate(selectedFiles); if (errorCode) { console.log(errorCode); e.preventDefault(); } } function handleFileSelect(e: any) { selectedFiles = [...selectedFiles, e.target.files[0]]; } // Create ref to DOM element let fileInput: HTMLInputElement; </script> <form action="?/submit" method="post" on:submit={handleSubmit}> <div> <label for="files">Click "+" to add file</label> <button on:click={(e) => {e.preventDefault(); fileInput.click();}}> <span>+</span> </button> <input type="file" accept={allowedExtensions} on:change={handleFileSelect} on:invalid={handleInvalid} bind:this={fileInput} hidden/> <input type="hidden" name="contest" value={contest.id} /> <input type="hidden" name="user" value={data.user.id} /> <input type="hidden" name="files" value={selectedFiles} /> <p>Selected files: {getFileNames(selectedFiles).join(', ')}</p> <br /> <button type="submit" class="btn btn-primary w-full">Submit</button> </div> </form>
实际提交结果
[Symbol(state)]: [ { name: 'files', value: '[object File],[object File],[object File]' }, { name: 'contest', value: '5bvod8teq8kh7yn' }, { name: 'user', value: 'dvkw0v6uz31zag2' }, { name: 'score', value: '0' } ]
期望提交结果
[Symbol(state)]: [ { name: 'files', value: [File] }, { name: 'files', value: [File] }, { name: 'files', value: [File] }, { name: 'contest', value: '5bvod8teq8kh7yn' }, { name: 'user', value: 'dvkw0v6uz31zag2' }, { name: 'score', value: '0' } ]
解决方案
核心问题是:用hidden输入框绑定File数组时,表单会自动将数组转为字符串,导致文件对象丢失。以下是两种可行解决方法:
方法1:手动构建FormData并通过fetch提交
完全接管表单提交逻辑,直接用FormData包装文件和字段,避免默认序列化问题:
<script lang="ts"> let selectedFiles: File[] = []; function handleSubmit(e: any) { e.preventDefault(); // 阻止表单默认提交 const errorCode = validate(selectedFiles); if (errorCode) { console.log(errorCode); return; } // 构建FormData const formData = new FormData(); // 添加普通字段 formData.append('contest', contest.id); formData.append('user', data.user.id); // 逐个添加文件(同名会自动生成多组键值对) selectedFiles.forEach(file => { formData.append('files', file); }); // 发送POST请求 fetch('?/submit', { method: 'POST', body: formData }) .then(res => { // 处理提交成功逻辑,比如跳转或提示 console.log('提交成功'); }) .catch(err => { console.error('提交失败:', err); }); } function handleFileSelect(e: any) { selectedFiles = [...selectedFiles, e.target.files[0]]; // 重置输入框,允许重复选择相同文件 e.target.value = ''; } let fileInput: HTMLInputElement; </script> <form on:submit={handleSubmit}> <div> <label for="files">Click "+" to add file</label> <button on:click={(e) => {e.preventDefault(); fileInput.click();}}> <span>+</span> </button> <input type="file" accept={allowedExtensions} on:change={handleFileSelect} on:invalid={handleInvalid} bind:this={fileInput} hidden/> <input type="hidden" name="contest" value={contest.id} /> <input type="hidden" name="user" value={data.user.id} /> <p>Selected files: {getFileNames(selectedFiles).join(', ')}</p> <br /> <button type="submit" class="btn btn-primary w-full">Submit</button> </div> </form>
注意:移除原代码中<input type="hidden" name="files" value={selectedFiles} />,这个输入框已无用。
方法2:动态添加隐藏文件输入框
保留表单默认提交逻辑,每次选择文件后,动态创建一个隐藏的file输入框并添加到表单中:
<script lang="ts"> let selectedFiles: File[] = []; function handleSubmit(e: any) { const errorCode = validate(selectedFiles); if (errorCode) { console.log(errorCode); e.preventDefault(); } } function handleFileSelect(e: any) { const file = e.target.files[0]; if (file) { selectedFiles = [...selectedFiles, file]; // 创建新的隐藏file输入框 const newFileInput = document.createElement('input'); newFileInput.type = 'file'; newFileInput.name = 'files'; newFileInput.hidden = true; // 手动设置文件(需用DataTransfer) const dataTransfer = new DataTransfer(); dataTransfer.items.add(file); newFileInput.files = dataTransfer.files; // 添加到表单 document.querySelector('form')?.appendChild(newFileInput); // 重置原输入框,允许重复选同个文件 e.target.value = ''; } } let fileInput: HTMLInputElement; </script> <form action="?/submit" method="post" on:submit={handleSubmit}> <div> <label for="files">Click "+" to add file</label> <button on:click={(e) => {e.preventDefault(); fileInput.click();}}> <span>+</span> </button> <input type="file" accept={allowedExtensions} on:change={handleFileSelect} on:invalid={handleInvalid} bind:this={fileInput} hidden/> <input type="hidden" name="contest" value={contest.id} /> <input type="hidden" name="user" value={data.user.id} /> <p>Selected files: {getFileNames(selectedFiles).join(', ')}</p> <br /> <button type="submit" class="btn btn-primary w-full">Submit</button> </div> </form>
内容的提问来源于stack exchange,提问作者Alaire Sena
相关产品推荐
相关产品推荐

