如何将文件上传至MongoDB或Mongoose并获取对应存储访问URL路径
实现Mongoose文件上传及访问URL生成方案
以下是实现pdf、文本等格式文件上传到Mongoose并生成可访问URL路径的完整方案:
方案选型
根据文件大小可以选择两种实现思路:
- 小于16MB的小文件:直接将文件二进制内容存入Mongoose字段,实现逻辑最简单
- 大于16MB的大文件:使用MongoDB原生GridFS分块存储,适配大文件存储要求
小文件上传实现(小于16MB)
1. 定义Mongoose存储Schema
const mongoose = require('mongoose'); const fileSchema = new mongoose.Schema({ fileName: { type: String, required: true }, fileType: { type: String, required: true }, // 存储文件二进制内容 fileData: { type: Buffer, required: true }, uploadTime: { type: Date, default: Date.now } }); const FileModel = mongoose.model('File', fileSchema);
2. 实现上传接口
使用multer中间件读取上传的文件内容,无需本地落盘直接存入数据库:
const multer = require('multer'); // 配置内存存储 const storage = multer.memoryStorage(); const upload = multer({ storage: storage }); app.post('/upload', upload.single('file'), async (req, res) => { try { const newFile = new FileModel({ fileName: req.file.originalname, fileType: req.file.mimetype, fileData: req.file.buffer }); await newFile.save(); // 生成可访问的URL路径,用文档_id作为唯一标识 const accessUrl = `${req.protocol}://${req.get('host')}/file/${newFile._id}`; res.json({ code: 0, url: accessUrl }); } catch (err) { res.status(500).json({ code: 1, msg: '上传失败' }); } });
3. 实现文件访问接口
匹配生成的URL规则,从数据库读取文件内容返回给前端:
app.get('/file/:id', async (req, res) => { try { const file = await FileModel.findById(req.params.id); if (!file) return res.status(404).send('文件不存在'); // 设置响应头,支持直接在浏览器展示文件 res.set({ 'Content-Type': file.fileType, 'Content-Disposition': `inline; filename="${file.fileName}"` }); res.send(file.fileData); } catch (err) { res.status(500).send('文件读取失败'); } });
大文件上传实现(大于16MB)
超过MongoDB单文档16MB大小限制的文件,使用GridFS分块存储实现:
const { GridFSBucket } = require('mongodb'); // 初始化GridFS存储桶 let gfs; mongoose.connection.once('open', () => { gfs = new GridFSBucket(mongoose.connection.db, { bucketName: 'largeFiles' }); }); // 大文件上传接口 app.post('/upload-large', upload.single('file'), (req, res) => { const writeStream = gfs.openUploadStream(req.file.originalname, { contentType: req.file.mimetype }); writeStream.end(req.file.buffer); writeStream.on('finish', () => { const accessUrl = `${req.protocol}://${req.get('host')}/file-large/${writeStream.id}`; res.json({ code: 0, url: accessUrl }); }); }); // 大文件访问接口 app.get('/file-large/:id', (req, res) => { try { const readStream = gfs.openDownloadStream(new mongoose.Types.ObjectId(req.params.id)); readStream.on('error', () => res.status(404).send('文件不存在')); readStream.pipe(res); } catch (err) { res.status(500).send('文件读取失败'); } });
注意事项
- 生产环境建议给文件访问接口增加权限校验,避免未授权访问
- 高频访问的文件不建议长期存在数据库,更推荐搭配对象存储服务使用,数据库仅存储文件URL
- 上传前建议增加文件类型、大小校验,避免非法文件入库
内容的提问来源于stack exchange,提问作者mcroche
相关产品推荐
相关产品推荐

