求助:使用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
相关产品推荐
相关产品推荐

