如何查看MongoDB中存储的超过1MB的Base64格式PDF文件
问题根因
- 主流浏览器对Data URL的长度限制为2~5MB,超过该长度的资源会被直接拦截,无法通过embed标签加载
- 将大体积PDF的Base64编码直接嵌入EJS页面,会导致HTML体积骤增、渲染阻塞,embed标签无法正常解析大段Base64内容
- 当前采用的Mongoose Buffer类型存储依赖MongoDB单BSON文档存储,单文档最大上限为16MB,后续文件体积继续增长仍会遇到瓶颈
临时修复方案(无需改造现有存储逻辑)
不需要修改现有存储逻辑,仅新增文件流接口,直接返回二进制PDF内容,避免将Base64嵌入页面:
1. 新增文件读取接口
在app.js中添加如下代码:
app.get('/file/:id', async (req, res) => { try { const file = await File.findById(req.params.id); if (!file) return res.status(404).send('文件不存在'); res.setHeader('Content-Type', file.fileType); // 如需默认下载而非嵌入,可添加以下响应头 // res.setHeader('Content-Disposition', `attachment; filename="${file.fileName}"`); res.send(file.file); } catch (err) { console.log(err); res.status(500).send('文件读取失败'); } });
2. 修改EJS渲染逻辑
将layout.ejs中的embed标签替换为接口地址引用,无需再使用虚拟属性生成的Data URL:
<% if(files) { %> <% files.forEach(m => { %> <embed type="application/pdf" src="/file/<%= m._id %>" width="300" height="300"> <!-- 可按需添加下载链接 --> <a href="/file/<%= m._id %>" download="<%= m.fileName %>">下载PDF</a> <% }) %> <% } %>
3. 调整bodyParser限制
将app.js中bodyParser的limit调整为你需要的最大文件大小(最大不超过16MB即可):
app.use(bodyParser.json({limit: "15mb"})); app.use(bodyParser.urlencoded({limit: "15mb",extended: true,parameterLimit: 5000}));
生产级方案(GridFS改造,支持任意大小PDF)
GridFS会将大文件拆分为256KB的块存储,不受单文档16MB限制,是MongoDB存储大文件的标准方案,完整实现如下:
1. 安装依赖
npm install multer gridfs-stream
2. 初始化GridFS实例
在app.js的数据库连接成功回调中添加GridFS初始化逻辑:
const Grid = require('gridfs-stream'); const multer = require('multer'); let gfs; db.on("open", ()=>{ console.log("database connection success"); // 初始化GridFS实例 gfs = Grid(db.db, mongoose.mongo); gfs.collection('pdfFiles'); // 自定义存储集合名 }); // 配置文件上传中间件 const storage = multer.memoryStorage(); const upload = multer({ storage: storage, fileFilter: (req, file, cb) => { if (file.mimetype === 'application/pdf') { cb(null, true); } else { cb(new Error('仅支持上传PDF格式文件'), false); } } });
3. 改造上传接口
app.post("/add", upload.single('fileUpload'), async (req,res)=>{ const fileName = req.body.fileName; const { buffer, mimetype } = req.file; // 将文件写入GridFS const writeStream = gfs.createWriteStream({ filename: fileName, content_type: mimetype, bucketName: 'pdfFiles' // 和初始化时的集合名保持一致 }); writeStream.write(buffer); writeStream.end(); writeStream.on('close', () => res.redirect('/')); writeStream.on('error', (err) => { console.log(err); res.status(500).send('文件上传失败'); }); });
4. 新增GridFS文件读取接口
app.get('/file/:filename', (req, res) => { gfs.files.findOne({ filename: req.params.filename }, (err, file) => { if (!file || file.length === 0) return res.status(404).send('文件不存在'); if (file.contentType !== 'application/pdf') return res.status(400).send('非PDF文件'); // 流式返回文件内容 const readStream = gfs.createReadStream(file.filename, { bucketName: 'pdfFiles' }); res.setHeader('Content-Type', file.contentType); readStream.pipe(res); }); });
5. 改造首页文件列表查询逻辑
将首页查询逻辑改为从GridFS的files集合中读取文件列表即可:
app.get("/",async(req,res,next)=>{ try{ gfs.files.find().toArray((err, files) => { res.render("layout", { files }); }); }catch (err){ console.log("err: "+ err); } });
EJS的渲染逻辑和临时方案保持一致即可。
内容的提问来源于stack exchange,提问作者user9114596
相关产品推荐
相关产品推荐

