SvelteKit Form Actions如何传递含文件的对象列表?
问题:SvelteKit Form Actions 结合 Superforms 上传带文件的对象数组
场景与问题描述
需要通过SvelteKit Form Actions向服务器发送一组包含文件的对象,对象结构定义如下:
export interface ExampleObjectWithFile { id: string, file: File, // 浏览器原生File类型 name: string, }
单独上传文件时可正常工作,但无法将文件作为FormData的一部分与对象其他字段关联发送。尝试过以下代码:
// 拦截formAction调用添加额外数据 async function onSubmit(input) { input.formData.append('parts', JSON.stringify($parts, skipFileReplacer)); for (let part of $parts) { input.formData.append('files', part.file); } }
服务器端仅收到空对象,后续发现通过await event.request.formData()).getAll("file")可获取文件列表,问题出在Superforms对文件列表的解析逻辑上。
咨询问题
- 文件能否作为FormData的一部分发送?
- 如何将文件列表发送至服务器并保留与对应对象的关联?
解答
问题1:文件能否作为FormData的一部分发送?
可以。你之前单独上传文件能正常工作已经证明这一点,问题并非FormData本身不支持文件,而是Superforms的默认解析逻辑无法直接识别你当前的FormData结构,导致解析后出现空对象。
问题2:如何保留文件与对应对象的关联?
推荐两种可靠实现方式,适配Superforms的解析逻辑:
方式1:结构化FormData键名(推荐)
通过给每个字段添加关联标识(比如数组索引),让Superforms能自动解析出关联结构:
// 客户端onSubmit拦截函数 async function onSubmit(input) { $parts.forEach((part, index) => { // 用数组索引作为关联标识,适配Superforms的数组解析 input.formData.append(`parts[${index}].id`, part.id); input.formData.append(`parts[${index}].name`, part.name); input.formData.append(`parts[${index}].file`, part.file); }); }
服务器端结合Superforms验证:
import { superValidate } from 'sveltekit-superforms/server'; import { z } from 'zod'; // 定义与对象结构匹配的Zod验证Schema const partSchema = z.object({ id: z.string(), name: z.string(), file: z.instanceof(File) // 直接验证File类型 }); const schema = z.array(partSchema); export const actions = { default: async ({ request }) => { // Superforms会自动解析结构化的FormData为数组 const form = await superValidate(request, schema); if (!form.valid) { return { form }; } // 验证通过后,form.data就是带文件关联的对象数组 console.log('关联后的对象数组:', form.data); return { form }; } };
方式2:分离元数据与文件,手动关联
将对象的非文件字段转成JSON字符串,文件按索引顺序上传,服务器端手动关联:
// 客户端onSubmit拦截函数 async function onSubmit(input) { // 提取非文件字段生成元数据JSON const partsMeta = JSON.stringify($parts.map(({ id, name }) => ({ id, name }))); input.formData.append('partsMeta', partsMeta); // 按数组索引追加文件,与元数据顺序一一对应 $parts.forEach((part, index) => { input.formData.append(`files[${index}]`, part.file); }); }
服务器端处理:
import { superValidate } from 'sveltekit-superforms/server'; import { z } from 'zod'; const partSchema = z.object({ id: z.string(), name: z.string(), file: z.instanceof(File) }); const schema = z.array(partSchema); export const actions = { default: async ({ request }) => { const formData = await request.formData(); const partsMeta = JSON.parse(formData.get('partsMeta') as string); const files = formData.getAll('files') as File[]; // 手动关联元数据与文件 const parts = partsMeta.map((meta, index) => ({ ...meta, file: files[index] })); // 将关联后的数组传给Superforms做验证 const form = await superValidate({ parts }, schema); return { form }; } };
内容的提问来源于stack exchange,提问作者Joel Bäckström
相关产品推荐
相关产品推荐

