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

Next.js 13中如何将FormData转为Strapi可识别的产品上传格式?

解决方案:分步将FormData转换为Strapi可识别格式并完成上传

Strapi的媒体字段(如主图、产品图)不直接接受原始File对象,必须先将图片上传到Strapi媒体库获取对应条目ID,再把这些ID关联到产品数据中。以下是具体实现步骤:

步骤1:在路由处理器中提取FormData数据

完善/api/createProduct/route.js,先从请求中分离文本字段和图片文件:

export async function POST(request) {
  const formData = await request.formData();
  
  // 提取文本/数字字段
  const productData = {};
  formData.forEach((value, key) => {
    if (typeof value !== 'object') { // 过滤File类型数据
      productData[key] = value;
    }
  });

  // 提取图片文件:主图和产品图数组
  const featuredImageFile = formData.get('featuredImage');
  const imageFiles = formData.getAll('images'); // 多次append的文件用getAll获取数组

  // 后续上传逻辑...
}

步骤2:上传图片到Strapi媒体库

Strapi v4上传API接受FormData格式的文件,需携带用户认证Cookie保持会话:

// 从环境变量取Strapi地址,需在.env中配置NEXT_PUBLIC_STRAPI_API_URL
const STRAPI_API_URL = process.env.NEXT_PUBLIC_STRAPI_API_URL;
// 获取请求Cookie,传递给Strapi维持认证状态
const cookies = request.headers.get('cookie');

// 通用上传函数:批量上传图片到Strapi
async function uploadToStrapi(files) {
  const uploadFormData = new FormData();
  // Strapi要求文件字段名为`files`,批量上传多次append
  files.forEach(file => uploadFormData.append('files', file));

  const uploadResponse = await fetch(`${STRAPI_API_URL}/api/upload`, {
    method: 'POST',
    headers: {
      Cookie: cookies,
    },
    body: uploadFormData,
  });

  if (!uploadResponse.ok) {
    throw new Error(`图片上传失败:${await uploadResponse.text()}`);
  }

  return uploadResponse.json(); // 返回媒体条目数组,每个条目含id、name等字段
}

// 上传所有图片(主图+产品图)
const allImageFiles = featuredImageFile ? [featuredImageFile, ...imageFiles] : imageFiles;
const uploadedMedia = await uploadToStrapi(allImageFiles);

步骤3:匹配主图与产品图的媒体ID

从上传返回的媒体条目中,对应提取主图和产品图的ID:

// 匹配主图ID:通过文件名匹配(react-dropzone返回的File对象带name字段)
let featuredImageId = null;
if (featuredImageFile) {
  const featuredMedia = uploadedMedia.find(media => media.name === featuredImageFile.name);
  featuredImageId = featuredMedia?.id;
}

// 收集产品图ID:排除主图对应的媒体条目
const imageIds = uploadedMedia
  .filter(media => !featuredImageFile || media.name !== featuredImageFile.name)
  .map(media => media.id);

步骤4:组装Strapi产品API所需数据格式

Strapi v4创建内容的API要求数据包裹在data对象中,关联媒体字段需传入ID对象/数组:

// 组装符合Strapi产品模型的数据结构
const strapiProductPayload = {
  data: {
    ...productData, // 传入文本/数字字段
    // 关联媒体字段:根据你的Strapi产品模型字段名调整
    featuredImage: featuredImageId ? { id: featuredImageId } : null, // 单媒体字段传{id: xxx}
    images: imageIds.map(id => ({ id })), // 多媒体字段传对象数组
  }
};

步骤5:调用Strapi产品创建API

将组装好的数据发送到Strapi产品创建接口,完成产品创建:

const createProductResponse = await fetch(`${STRAPI_API_URL}/api/products`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Cookie: cookies, // 维持认证
  },
  body: JSON.stringify(strapiProductPayload),
});

if (!createProductResponse.ok) {
  throw new Error(`产品创建失败:${await createProductResponse.text()}`);
}

const createdProduct = await createProductResponse.json();

// 返回结果给客户端
return Response.json({ status: 'success', product: createdProduct });

完整路由处理器代码(含错误处理)

export async function POST(request) {
  try {
    const formData = await request.formData();
    const STRAPI_API_URL = process.env.NEXT_PUBLIC_STRAPI_API_URL;
    const cookies = request.headers.get('cookie');

    // 1. 提取文本字段
    const productData = {};
    formData.forEach((value, key) => {
      if (typeof value !== 'object') {
        productData[key] = value;
      }
    });

    // 2. 提取图片文件
    const featuredImageFile = formData.get('featuredImage');
    const imageFiles = formData.getAll('images');

    // 3. 上传图片到Strapi
    async function uploadToStrapi(files) {
      const uploadFormData = new FormData();
      files.forEach(file => uploadFormData.append('files', file));
      
      const res = await fetch(`${STRAPI_API_URL}/api/upload`, {
        method: 'POST',
        headers: { Cookie: cookies },
        body: uploadFormData,
      });

      if (!res.ok) throw new Error(await res.text());
      return res.json();
    }

    const allImageFiles = featuredImageFile ? [featuredImageFile, ...imageFiles] : imageFiles;
    const uploadedMedia = await uploadToStrapi(allImageFiles);

    // 4. 匹配媒体ID
    let featuredImageId = null;
    if (featuredImageFile) {
      const featuredMedia = uploadedMedia.find(m => m.name === featuredImageFile.name);
      featuredImageId = featuredMedia?.id;
    }
    const imageIds = uploadedMedia
      .filter(m => !featuredImageFile || m.name !== featuredImageFile.name)
      .map(m => m.id);

    // 5. 创建产品
    const strapiPayload = {
      data: {
        ...productData,
        featuredImage: featuredImageId ? { id: featuredImageId } : null,
        images: imageIds.map(id => ({ id })),
      }
    };

    const createRes = await fetch(`${STRAPI_API_URL}/api/products`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Cookie: cookies,
      },
      body: JSON.stringify(strapiPayload),
    });

    if (!createRes.ok) throw new Error(await createRes.text());
    const createdProduct = await createRes.json();

    return Response.json({ status: 'success', product: createdProduct });
  } catch (error) {
    return Response.json({ status: 'error', message: error.message }, { status: 500 });
  }
}

注意事项

  • 确保Strapi产品模型中,featuredImage设为单媒体类型,images设为多媒体类型。
  • 若使用JWT认证,需从Cookie提取token并替换为Authorization: Bearer <token>头;会话认证直接传Cookie即可。
  • 文件名匹配存在重复风险,可在客户端给文件添加唯一标识,或通过文件大小、类型辅助匹配。

内容的提问来源于stack exchange,提问作者Kofi Talent

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 18:55:17