You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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对文件列表的解析逻辑上。

咨询问题

  1. 文件能否作为FormData的一部分发送?
  2. 如何将文件列表发送至服务器并保留与对应对象的关联?

解答

问题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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 20:12:41