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

求助:使用GridFS向MongoDB上传音频时无法返回file._id

问题:GridFS保存音频Blob至MongoDB后无法返回file._id

函数此前可正常运行,现在数据已成功写入数据库,但file._id始终无法返回——close和finish事件回调中的file参数始终为undefined。函数及调用逻辑如下:

原函数代码

function saveAudioToGridFS(audioBlob) {
  return new Promise((resolve, reject) => {
    const gridFSBucket = new mongoose.mongo.GridFSBucket(conn.db),
          upldName = getAudioName() // 获取文件名

    const writeStream = gridFSBucket.openUploadStream(upldName);

    writeStream.on('close', (file) => {
      // 此处file始终为undefined
      if (file !== undefined) {
        console.log("file=(saveAudioToGridFS)",file)
        console.log("file._id=",file._id)
        resolve(file._id); // 返回GridFS文件ID
      }
    });

    writeStream.on('finish', (file) => {
      // 此处file始终为undefined
      if (file !== undefined) {
        console.log("file=(finish)",file)
        console.log("file._id=",file._id)
        resolve(file._id); // 返回GridFS文件ID
      }
    });

    writeStream.on('error', (error) => {
      reject(error);
    });

  // 使用streamifier将Blob转为可读流
  const readableStream = streamifier.createReadStream(audioBlob);
  readableStream.pipe(writeStream);
  });
} /* End of saveAudioToGridFS */

调用逻辑

server.post('/upload', async (req, res) => {
  try {
    if (!req.body.audio) {
      return res.status(400).json({ message: 'No audio data uploaded.' });
    }
  
    const audioBuffer = Buffer.from(req.body.audio, 'base64'),
          fileId = await saveAudioToGridFS(audioBuffer);
    .....
  } catch (error) {
    res.status(500).json({
      message: 'An error occurred during upload.',
      error: JSON.stringify(error)
    });
  }
});

解决方案

问题根源

MongoDB Node.js驱动的GridFSBucket上传流,其close和finish事件不会传递file对象——这是驱动API的设计规则,并非代码bug。原代码错误依赖事件回调的file参数,且未处理file为undefined时的resolve逻辑,导致Promise一直处于pending状态,调用端无法获取返回值。

修复后的函数代码

直接从上传流对象writeStream中获取id属性(该属性在流创建时已生成,对应GridFS文件的_id),同时移除冗余的finish事件监听,确保Promise一定会resolve:

function saveAudioToGridFS(audioBlob) {
  return new Promise((resolve, reject) => {
    const gridFSBucket = new mongoose.mongo.GridFSBucket(conn.db),
          upldName = getAudioName();

    const writeStream = gridFSBucket.openUploadStream(upldName);

    // 仅监听close事件(GridFS上传完成的最终事件)
    writeStream.on('close', () => {
      console.log("file._id=", writeStream.id);
      resolve(writeStream.id);
    });

    writeStream.on('error', (error) => {
      reject(error);
    });

    const readableStream = streamifier.createReadStream(audioBlob);
    readableStream.pipe(writeStream);
  });
}

更简洁的Async/Await版本

利用Node.js的stream/promises模块简化流处理,避免回调嵌套:

const { pipeline } = require('stream/promises');

async function saveAudioToGridFS(audioBlob) {
  const gridFSBucket = new mongoose.mongo.GridFSBucket(conn.db),
        upldName = getAudioName();

  const writeStream = gridFSBucket.openUploadStream(upldName);
  const readableStream = streamifier.createReadStream(audioBlob);

  // 用pipeline处理流,自动处理错误和完成逻辑
  await pipeline(readableStream, writeStream);
  
  return writeStream.id;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 23:25:56