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

Nuxt 3.0.0正式版文件上传功能失效问题求助

文件上传问题修复方案

核心错误原因

  • 前后端字段不匹配:前端FormData追加的字段名是file,后端却尝试读取files.photo,导致files.photo为undefined,触发mimetype读取错误。
  • Nuxt 3环境下req对象获取错误:正式版Nuxt 3中,请求对象并非event.req,而是event.node.req。
  • fs直接写入文件损坏:readBody会将请求体解析为JSON格式,无法正确获取二进制文件内容,导致写入文件损坏。

修正后的后端代码(formidable版本)

import formidable from "formidable";
import fs from "fs";
import path from "path";

export default defineEventHandler(async (event) => {
  // 确保uploads目录存在
  const uploadDir = path.join(process.cwd(), "public", "uploads");
  if (!fs.existsSync(uploadDir)) {
    fs.mkdirSync(uploadDir, { recursive: true });
  }

  return new Promise((resolve, reject) => {
    const form = formidable({ multiples: true });
    // Nuxt 3正式版中使用event.node.req
    form.parse(event.node.req, (err, fields, files) => {
      if (err) {
        return reject({ status: "error", message: err.message });
      }

      // 匹配前端的字段名file,处理单个/多个文件情况
      const uploadFile = Array.isArray(files.file) ? files.file[0] : files.file;
      if (!uploadFile) {
        return resolve({
          status: "error",
          message: "请上传名为file的文件"
        });
      }

      // 校验图片类型
      if (!uploadFile.mimetype.startsWith("image/")) {
        return resolve({
          status: "error",
          message: "仅支持上传图片类型文件"
        });
      }

      // 生成唯一文件名
      const imageName = `${Date.now()}${Math.round(Math.random() * 100000)}${uploadFile.originalFilename}`;
      const oldPath = uploadFile.filepath;
      const newPath = path.join(uploadDir, imageName);
      // 正确的访问URL
      const imageUrl = `/uploads/${imageName}`;

      try {
        fs.copyFileSync(oldPath, newPath);
        resolve({ status: "ok", url: imageUrl });
      } catch (copyErr) {
        reject({ status: "error", message: copyErr.message });
      }
    });
  });
});

前端代码优化(类型修正)

<template>
  <v-col cols="12" sm="10">
    <v-sheet min-height="70vh" rounded="lg" class="pa-3">
      <input
        type="file"
        accept="image/*"
        label="Bild hochladen"
        @change="onChange"
      />
    </v-sheet>
  </v-col>
</template>

<script setup lang="ts">
const onChange = async (e: Event) => {
  // 类型断言确保e.target是input元素
  const input = e.target as HTMLInputElement;
  const selectedFile = input.files?.[0];
  if (!selectedFile) return;

  const formData = new FormData();
  formData.append('file', selectedFile);

  try {
    await $fetch(`/api/v1/data/images`, {
      method: 'POST',
      body: formData,
    });
    alert('上传成功');
  } catch (err) {
    console.error('上传失败:', err);
    alert('上传失败,请重试');
  }
}
</script>

fs模块直接处理文件的正确方式

不能用readBody解析二进制文件,需通过流式读取请求体:

import fs from "fs";
import path from "path";
import { createWriteStream } from "fs";

export default defineEventHandler(async (event) => {
  const { imageName } = event.context.params;
  const savePath = path.join(process.cwd(), "assets", "article", "images", imageName);
  
  // 确保目录存在
  const saveDir = path.dirname(savePath);
  if (!fs.existsSync(saveDir)) {
    fs.mkdirSync(saveDir, { recursive: true });
  }

  return new Promise((resolve, reject) => {
    const writeStream = createWriteStream(savePath);
    // 流式传输请求体到文件
    event.node.req.pipe(writeStream);
    writeStream.on('finish', () => {
      resolve({ status: "ok", message: "文件保存成功" });
    });
    writeStream.on('error', (err) => {
      reject({ status: "error", message: err.message });
    });
  });
});

注意:此方式需确保前端请求直接发送二进制数据,而非FormData;若用FormData,仍需用formidable解析。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 14:55:20