GridFS 文件总长度必须从 files 集合查询,不能依赖流对象的 length 属性;正确做法是通过 gridFSBucket.findOne({ _id: fileId }) 获取文件文档的 length 字段,再在 data 事件中累加 chunk.length 计算实时进度。
GridFS 文件流没有内置 length 属性,不能直接用
GridFS 存储的文件被切分成多个
,每个 chunk 默认 255KB,
集合里存的
字段才是完整文件大小(单位
字节
),但这个值不会自动挂到 Node.js 的
实例上。常见错误是试图读
或
—— 这些在 GridFS 流中都是
。
正确做法是:先查
集合拿到目标文件的
,再在流读取过程中累加已读字节数。
如何安全获取 GridFS 文件的总长度
必须通过
或
的
查
文档,不能依赖流对象本身。
使用
,取返回数组首项的
字段
或更高效:用
(注意不是
的返回值)
如果用的是
查询,需确保唯一性,否则可能匹配多个文件,
取值不可靠
注意:MongoDB 驱动 v4+ 中
返回 Promise,需
;v3 则是回调风格
监听 data 事件累加已读字节数并计算进度
Node.js
的
事件每次触发时传入一个
,它的
就是本次读取的字节数。把每次的字节数累加,除以总
,就能得到实时进度(0–1)。
byte-units-master转换字节单位的PHP库
byte-units-master转换字节单位的PHP库
下载
示例关键逻辑:
不要用
—— 它只反映内部缓冲区剩余可读字节数,不是累计值
避免在
回调里做重操作(如频繁 DOM 更新),可用
或节流
如果文件极小(data,进度会直接跳到 100%
注意分块边界与流暂停行为对进度平滑性的影响
GridFS 流底层按 chunk 拉取,网络延迟或磁盘 IO 可能导致
事件间隔不均,造成进度条“卡顿”。这不是计算错误,而是真实读取节奏。
若需更平滑的 UI 进度,可在前端用插值(如 requestAnimationFrame + 上次进度 → 目标进度)过渡,而非完全依赖后端事件频率
调用
/
不影响
累加逻辑,但会中断事件流,暂停期间进度停滞
如果服务端启用了压缩(如 gzip),GridFS 不处理,所以
和实际传输字节数不同 —— 但进度条应基于解压后的内容长度,即
,这是正确的
真正容易被忽略的是:很多项目直接拿
当普通 HTTP 流处理,忘了 GridFS 的元数据必须单独查一次,而且这个查询和流打开是两个独立异步操作,没等
返回就开流,会导致初始进度 NaN。
stream.lengthchunksfileslengthReadableStreamstream.lengthstream.headers['content-length']undefinedfileslengthgridFSBucket.find()gridFSBucket.openDownloadStream()idfilesgridFSBucket.find({ _id: fileId }).toArray()lengthgridFSBucket.findOne({ _id: fileId })openDownloadStreamfilenamelengthfindOneawaitReadableStreamdataBuffer.lengthlength
const totalLength = fileDoc.length; // 从 files 集合查出
let bytesRead = 0;
stream.on('data', (chunk) => {
bytesRead += chunk.length;
const progress = bytesRead / totalLength; // 0.0 ~ 1.0
console.log(Progress: ${(progress * 100).toFixed(1)}%);
});
stream.on('end', () => {
console.log('Done. bytesRead === totalLength:', bytesRead === totalLength);
});
stream.readableLengthdatarequestIdleCallbackdatastream.pause()resume()bytesReadlengthfiles.lengthstreamlength