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

Next.js中使用formidable遇API未返回响应问题求助

解决Next.js API路由API resolved without sending a response问题

问题原因

你的async handler函数会在form.parse的回调执行前提前结束,Next.js检测到函数执行完毕但未在主执行流内返回响应(回调里的响应属于异步触发,不在handler的执行周期内),因此抛出该警告。即便前端能收到响应,Node.js的请求上下文已被提前标记为完成,导致警告出现。

修复方案:用Promise包裹异步操作

将回调式的form.parse和流式上传逻辑转为Promise,让async handler等待整个流程完成,确保响应在handler执行周期内发送。

修改后的完整代码:

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

export default async function handler(req, res) {
  const { query: { uuid } } = req
  const token = await getToken({ req })
  const key = JSON.parse(process.env.GOOGLE_APPLICATION_CREDENTIALS.toString())

  if (!token) {
    return res.status(401).json({ error: '用户必须登录才能执行此操作' })
  }

  const storage = new Storage({
    projectId: process.env.PROJECT_ID,
    credentials: {
      client_email: key.client_email,
      private_key: key.private_key.replace(/\\n/g, '\n')
    }
  })

  const form = formidable({
    keepExtensions: true,
  })

  try {
    // 包裹form.parse为Promise
    const [fields, files] = await new Promise((resolve, reject) => {
      form.parse(req, (err, fields, files) => err ? reject(err) : resolve([fields, files]))
      form.on('error', reject)
    })

    const selectedFile = files.file[0];
    if (!selectedFile) {
      console.error('未上传文件')
      return res.status(400).json({ error: '未上传文件' })
    }

    const salt = crypto.randomBytes(16).toString('hex')
    const hashedFilename = crypto.createHash('sha256').update(selectedFile.originalFilename + salt).digest('hex')

    const bucket = storage.bucket(process.env.CLOUD_STORAGE_BUCKET_NAME)
    const blob = bucket.file(hashedFilename)

    // 包裹流式上传为Promise
    await new Promise((resolve, reject) => {
      const blobStream = createReadStream(selectedFile.filepath)
        .pipe(blob.createWriteStream({
          resumable: false,
          contentType: selectedFile.mimetype
        }))

      blobStream.on('error', (err) => {
        console.error(`云存储上传出错: ${err}`)
        cleanupStreams(blobStream, selectedFile.filepath)
        reject(err)
      })

      blobStream.on('finish', resolve)
    })

    await blob.setMetadata({
      metadata: {
        UUID: uuid,
        FILE_NAME: selectedFile.originalFilename,
        CREATION_DATE: getDate(),
      }
    })

    cleanupStreams(blobStream, selectedFile.filepath)
    return res.status(201).json({ message: `文件上传成功: ${selectedFile.newFilename}` })
  } catch (err) {
    console.error(`请求处理出错: ${err}`)
    // 避免重复发送响应
    if (!res.headersSent) {
      const statusCode = err.message.includes('No file uploaded') ? 400 : 500
      const errorMsg = err.message || '服务器内部错误'
      return res.status(statusCode).json({ error: errorMsg })
    }
  }
}

核心调整说明

  1. Promise化异步操作:将form.parse和流式上传的回调逻辑转为Promise,用await等待所有异步步骤完成,确保handler不会提前退出。
  2. 统一错误处理:通过try/catch捕获所有异常,确保错误场景下也能返回合法响应,同时检查res.headersSent避免重复发送响应。
  3. 同步执行流:让整个上传流程处于handler的主执行流中,符合Next.js对API路由的响应要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 09:06:08