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

Next.js API路由转发文件至Strapi上传接口失败,Strapi返回“Files are empty”错误

Next.js API路由转发文件至Strapi上传接口失败,Strapi返回“Files are empty”错误

看起来你卡在这个文件转发问题上已经两周了,太折磨人了!我帮你梳理下可能的问题点和解决方案,应该能帮你搞定这个恼人的错误。

可能的问题原因及对应解决方案

1. Formidable解析后的文件结构不符合预期

你代码里直接用了files.files.filepath,但Formidable解析文件后的结构可能和你想的不一样:如果前端React Dropzone上传的是单个文件,files.files可能是一个对象;如果是多文件上传,它会变成数组。

先在代码里加个日志确认实际结构:

console.log('Formidable解析后的文件结构:', files);

如果输出是类似{ files: [File] }的数组格式,就得调整取文件的方式:

const targetFile = Array.isArray(files.files) ? files.files[0] : files.files;
if (!targetFile) {
  return res.status(400).json({ error: '未找到上传的文件' });
}

之后用targetFile.filepath来创建文件流就对了。

2. Formidable临时文件被过早删除

Formidable默认会把上传文件存在系统临时目录,并且在请求结束后自动删除。当你用fs.createReadStream读取文件并通过fetch转发时,可能文件已经被删掉了,导致Strapi接收到空内容。

解决方法是修改Formidable的配置,关闭自动删除并指定自定义临时目录:

const form = new formidable.IncomingForm({
  keepExtensions: true, // 保留文件扩展名
  autoDelete: false, // 禁止自动删除临时文件
  uploadDir: './tmp', // 指定自定义临时目录(记得提前在项目根目录创建这个文件夹)
});

文件转发完成后,记得手动删除临时文件避免堆积:

// 在获取imageData之后执行
fs.unlinkSync(targetFile.filepath);

3. 文件流传递的潜在问题

有时候直接用文件流传递可能因为流未正确读取就被发送,导致Strapi接收不到内容。对于头像这类小文件,可以改用读取文件Buffer的方式传递,更稳定:

// 替代createReadStream的方式
const fileBuffer = fs.readFileSync(targetFile.filepath);
formData.append('files', fileBuffer, targetFile.originalFilename);

4. 确认Strapi上传参数的正确性

再核对下传给Strapi的几个关键参数:

  • ref: 'plugin::users-permissions.user':确认Strapi用户模型的UID是否正确(默认是这个,若自定义过要对应修改)
  • refId: user_id:确保user_id是有效的用户ID,且当前JWT用户有权限修改该用户
  • field: 'profile_picture':确认用户模型中确实存在名为profile_picture的媒体类型字段

修改后的完整API路由代码示例

整合了上述修改后的代码,你可以直接参考:

import formidable from 'formidable';
import FormData from 'form-data';
import fs from 'fs';
import path from 'path';

// 确保临时目录存在
const tmpDir = path.join(process.cwd(), 'tmp');
if (!fs.existsSync(tmpDir)) {
  fs.mkdirSync(tmpDir);
}

export default async function handler(req, res) {
  const { cookies: { jwt }, query: { user_id } } = req;

  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  const form = new formidable.IncomingForm({
    keepExtensions: true,
    autoDelete: false,
    uploadDir: tmpDir,
  });

  try {
    const { fields, files } = await new Promise((resolve, reject) => {
      form.parse(req, (err, fields, files) => {
        if (err) reject(err);
        else resolve({ fields, files });
      });
    });

    // 处理用户字段更新
    if (Object.keys(fields).length) {
      const resp = await fetch(`${process.env.NEXT_PUBLIC_BACKEND_URL}/api/users/${user_id}`, {
        method: "PUT",
        headers: {
          Authorization: `Bearer ${jwt}`,
          "Content-Type": "application/json"
        },
        body: JSON.stringify(fields)
      });
      const data = await resp.json();
      console.log('Fields data', data);
    }

    // 处理文件上传
    const targetFile = Array.isArray(files.files) ? files.files[0] : files.files;
    if (targetFile) {
      console.log('准备上传文件:', targetFile.originalFilename);

      const formData = new FormData();
      // 用Buffer方式传递文件
      const fileBuffer = fs.readFileSync(targetFile.filepath);
      formData.append('files', fileBuffer, targetFile.originalFilename);
      formData.append('ref', 'plugin::users-permissions.user');
      formData.append('refId', user_id);
      formData.append('field', 'profile_picture');

      const imageResp = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/upload`, {
        method: "POST",
        headers: {
          Authorization: `Bearer ${jwt}`,
          // 不要手动设置Content-Type,FormData会自动生成带boundary的正确值
        },
        body: formData
      });

      const imageData = await imageResp.json();
      console.log('imageData', imageResp.status, imageData);

      // 删除临时文件
      fs.unlinkSync(targetFile.filepath);
    }

    res.status(200).json({ fields, files });
  } catch (err) {
    console.error('处理请求出错:', err);
    res.status(500).json({ error: err.message });
  }
}

export const config = {
  api: {
    bodyParser: false,
  },
};

备注:内容来源于stack exchange,提问作者Kofi Talent

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.22 12:10:29