跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

如何在MongoDB GridFS中存取PDF文档

根本原因是PDF二进制流被误作字符串处理,导致非ASCII字节错误解码;必须用Buffer/Uint8Array原始读取、设置contentType、显式构造GridFSBucket、正确处理下载流与ObjectId类型转换。 GridFS 存 PDF 时为什么文件变大或损坏 根本原因通常是把 PDF 的二进制流误当成字符串处理,比如用
fs.readFileSync(path, 'utf8')
读取后直接传给
gridFSBucket.uploadFromStream()
—— 这会让非 ASCII 字节被错误解码,PDF 头部(如
%PDF-1.4
)一乱,下游就打不开。 必须用
Buffer
或
Uint8Array
原始二进制方式读取:例如
fs.readFileSync(path)
(不带编码参数)或
fs.createReadStream(path)
上传时指定
contentType: 'application/pdf'
,否则默认是
application/octet-stream
,某些前端库可能不自动触发 PDF 预览 别在上传前对 Buffer 做
.toString()
、
JSON.stringify()
或任何字符串转换 用 Node.js MongoDB Driver v5+ 上传 PDF 到 GridFS v5+ 的
MongoClient
默认启用新驱动 API,
GridFSBucket
不再从
db
上直接挂载,需显式构造;同时
uploadFromStream()
返回的是 Promise,不是 callback。 初始化 bucket 要传
{ bucketName: 'pdfs' }
显式命名,否则默认用
fs
,容易和别的业务冲突 上传流必须是可读流(
fs.createReadStream()
)或
Buffer
,不能是路径字符串 推荐加
metadata
字段存业务信息,比如
{ originalName: 'report.pdf', userId: 'u123' }
,后续查起来方便
const stream = fs.createReadStream('./report.pdf'); const uploadStream = bucket.uploadFromStream( 'report.pdf', stream, { contentType: 'application/pdf', metadata: { originalName: 'report.pdf', userId: 'u123' } } ); const { _id } = await uploadStream;
根据 fileId 查 PDF 并返回给 HTTP 响应 常见错误是调用
bucket.openDownloadStream()
后没设响应头,或者用
toArray()
把整个文件加载进内存——PDF 动辄几 MB,Node.js 进程容易 OOM。 必须用
res.setHeader('Content-Type', 'application/pdf')
和
res.setHeader('Content-Disposition', 'inline; filename="...")
,否则浏览器可能下载而非预览 直接把 download stream
pipe
给
res
,不要
toArray()
或
read()
注意处理文件不存在:当
bucket.openDownloadStream(fileId)
返回 null,要手动
res.status(404).end()
const downloadStream = bucket.openDownloadStream(fileId); if (!downloadStream) { return res.status(404).end(); } res.setHeader('Content-Type', 'application/pdf'); res.setHeader('Content-Disposition', 'inline; filename="report.pdf"'); downloadStream.pipe(res);
用 ObjectId 查询 GridFS 文件元数据但查不到
files
集合里存的
_id
是
ObjectId
,但如果你用字符串(比如 URL 参数里传的
"65a1b2c3d4e5f67890123456"
)直接查,MongoDB 不会自动转类型,结果为空。 查询前必须用
new ObjectId(idString)
转换,且确保
idString
是合法 24 位十六进制 查元数据用
bucket.find({ _id: objectId })
,不是
collection.find()
—— GridFS 元数据存在
pdfs.files
集合,但 driver 封装了这层细节 如果用
findOne()
,记得
await
,它的返回值是文档对象,含
length
、
uploadDate
、
metadata
等字段 最容易被忽略的是:GridFS 的
files
和
chunks
集合名默认是
fs.files
和
fs.chunks
,但如果你初始化 bucket 时指定了
bucketName: 'pdfs'
,那实际集合就是
pdfs.files
和
pdfs.chunks
—— 直接去 shell 里查错地方,永远找不到。

相关文章