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

NodeJS+Express+Mongo+IPFS多步Post路由高效实现与问题排查

问题排查与最优实现方案

核心问题定位

  • 第一,你用全局EventEmitter绑定事件的实现逻辑完全错误:每次请求进入路由都会给emitter新增一组on监听器,多次请求后监听器会重复触发,不同请求的变量还会互相污染,完全不能用来控制单请求的异步流程。而且你用的是on而非once,监听器会一直驻留内存,既会造成内存泄漏,也会导致后续请求的流程完全混乱。
  • 第二,异步操作没有等待前序IO完全完成就执行下一步:你遇到的IPFS上传文件只有6字节、哈希固定的问题,90%以上概率是加密函数还没把完整的加密文件写入磁盘,你就已经开始读取文件上传IPFS,读到的是空文件或者不完整的临时文件,所以哈希永远相同。
  • 第三,异步错误完全没有捕获:任意一步执行出错都会导致请求卡住超时,客户端收不到响应。

最优多步异步路由实现方案

你已经在用async/await语法,完全不需要用EventEmitter控制流程,直接把回调式API转成Promise后按顺序串行执行即可,逻辑清晰且没有副作用。

优化后代码

const fs = require('fs/promises')
const path = require('path')

// 先把回调形式的file.mv转成Promise
const moveFile = (file, targetPath) => {
  return new Promise((resolve, reject) => {
    file.mv(targetPath, err => err ? reject(err) : resolve())
  })
}

router.post('/', async(req, res) => {
  // 提前定义需要清理的文件路径,出错时统一清理
  let uploadFilePath = null
  let encryptFilePath = null

  try {
    // 1. 参数校验
    if (!req.session.loggedIn || !req.files) {
      return res.status(400).json({ message: 'Bad Request received check your request again, bruh.' })
    }

    // 2. 处理文件名,移动文件到uploads目录
    const rawFile = req.files.file
    const ext = path.extname(rawFile.name)
    const fileName = req.body.filename + ext
    uploadFilePath = path.join('./uploads', fileName)
    await moveFile(rawFile, uploadFilePath)

    // 3. 加密文件,等待加密完全完成
    const authKey = req.session.authKey
    encryptFilePath = path.join('./tmp', `${fileName}.enc`)
    // 注意:确保你的encrypt函数是真·异步函数,只有文件完全写入磁盘后才resolve
    await encrypt({ file: uploadFilePath, password: authKey })

    // 4. 上传加密文件到IPFS,先校验文件是否存在且大小正常
    const encFileStat = await fs.stat(encryptFilePath)
    console.log(`待上传加密文件大小:${encFileStat.size}字节`)
    // 修正你原来的传参错误,只传正确的加密文件路径
    const fileHash = await addFile(encryptFilePath)

    // 5. 写入MongoDB
    await db.IPFS.create({
      owner: db.User._id,
      title: fileName,
      content: req.body.content,
      dbCID: fileHash,
      tags: req.body.tags
    })

    // 6. 清理临时文件
    await Promise.allSettled([
      fs.unlink(uploadFilePath),
      fs.unlink(encryptFilePath)
    ])

    // 7. 返回响应
    res.redirect('/recap')
  } catch (err) {
    console.error('路由执行出错:', err)
    // 出错时也要清理已经生成的临时文件,避免磁盘占用
    if (uploadFilePath) await fs.unlink(uploadFilePath).catch(() => {})
    if (encryptFilePath) await fs.unlink(encryptFilePath).catch(() => {})
    res.status(500).json({ message: '服务端处理失败', error: err.message })
  }
})

剩余问题排查方向

如果修改后还是出现IPFS文件大小异常的问题,按以下顺序排查:

  • 先注释掉IPFS上传和后续清理逻辑,看./tmp目录下生成的.enc文件大小是否正常,如果大小异常,修改你的encrypt函数,确保加密文件完全写入磁盘后再resolve Promise,不要在文件流还没关闭的时候就返回。
  • 如果加密文件大小正常,检查你的addFile函数,确认你是读取传入的路径对应的文件内容上传,而非读取了固定路径的空文件/错误文件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 07:36:02