Express+GridFS接口报Cannot read properties of undefined(reading 'files')
问题根因
- 初始化时序错误:原有代码先启动Express服务监听端口,再等Mongo连接open回调里给
gfs赋值。请求打到/files接口时,大概率gfs还处于初始的undefined状态,直接触发Cannot read properties of undefined (reading 'files')报错。只有极少数请求刚好卡在gfs赋值完成后到达,才会出现“偶尔能正常展示列表、支持删除”的概率性正常现象。 - GridFS API混用:代码同时引入了老版第三方
gridfs-stream库和Mongoose自带的官方GridFSBucket两套操作接口,且老版gridfs-stream的gfs.collection('media')调用没有接收返回值,查询逻辑不生效,才会出现接口返回空对象{}的问题。编辑器识别到顶层定义的gfs初始无值、后续属性调用不合法,才会出现files关键字无语法高亮的现象。 - 缺少就绪态校验:路由逻辑里没有判断GridFS实例是否初始化完成,服务刚启动阶段的请求会直接抛错。
修复方案
- 调整初始化时序,移除冗余的
gridfs-stream依赖,统一使用Mongoose官方自带的GridFSBucketAPI,等Mongo连接、GridFS存储桶全部初始化完成后再启动HTTP服务:
// 移除原有let gfs相关的gridfs-stream初始化逻辑 let gridfsBucket; const conn = mongoose.connection; conn.once('open', () => { // 初始化media存储桶 gridfsBucket = new mongoose.mongo.GridFSBucket(conn.db, { bucketName: 'media' }); // 把原有app.listen逻辑移到这里,确保存储就绪后再对外提供服务 app.listen(8000, () => { console.log('Server is running on : http://localhost:8000'); console.log('MongoDB is connected!'); }); });
- 修正
/files文件列表接口,增加就绪态校验,用官方API查询文件:
app.get('/files',async(req,res)=>{ try{ // 存储未就绪时直接返回提示,避免抛undefined错误 if (!gridfsBucket) { return res.status(503).send('存储服务初始化中,请稍后重试'); } const files = await gridfsBucket.find().toArray() res.json(files) }catch(err){ console.log(err) console.log("unable to fetch files") res.status(400).send(err.message) } })
- 新增支持范围请求的视频流播放接口,实现点击链接流式加载、支持进度条拖动:
app.get('/video/:filename', async (req, res) => { if (!gridfsBucket) { return res.status(503).send('存储服务初始化中,请稍后重试'); } const [file] = await gridfsBucket.find({ filename: req.params.filename }).toArray(); if (!file) return res.status(404).send('视频文件不存在'); // 解析HTTP Range请求头,实现分段流式传输 const range = req.headers.range; if (!range) return res.status(400).send('需要Range请求头支持'); const [startStr, endStr] = range.replace(/bytes=/, '').split('-'); const start = parseInt(startStr, 10); const end = endStr ? parseInt(endStr, 10) : file.length - 1; const chunkLength = end - start + 1; res.writeHead(206, { 'Content-Range': `bytes ${start}-${end}/${file.length}`, 'Accept-Ranges': 'bytes', 'Content-Length': chunkLength, 'Content-Type': file.contentType || 'video/mp4' }); // 创建读取流直接管道输出到响应 const readStream = gridfsBucket.openDownloadStreamByName(file.filename, { start, end }); readStream.pipe(res); });
- 前端无需调整拉取文件列表的逻辑,渲染视频列表时将视频标签的
src属性指向http://localhost:8000/video/[对应文件的filename]即可正常播放。
内容的提问来源于stack exchange,提问作者jayshinobi
相关产品推荐
相关产品推荐

