Node.js+MongoDB GridFS文件上传后下载删除功能实现问题
MongoDB GridFS 文件下载/删除功能实现
现有代码说明
我已在本地服务通过server.js实现MongoDB文件上传功能,server.js现有代码如下:
// 已替换个人ID和密码 const mongoURI = "mongodb+srv://myID:myPW@cluster0.0ct4j.mongodb.net/test"; // Mongo连接 const conn = mongoose.createConnection(mongoURI); // 初始化gfs let gfs; conn.once('open', () =>{ gfs = Grid(conn.db, mongoose.mongo); // GridFsStorage默认使用fs作为集合名,这里需要对齐配置 gfs.collection('fs'); }); // 中间件 server.use(bodyParser.json()); server.use(methodOverride('_method')); // GridFS存储配置 const storage = new GridFsStorage({ url: mongoURI, cache: true, file: (req, file) => { return file.originalname + Date.now(); } }); const upload = multer({ storage }); server.get("/upload", (req,res) =>{ res.render('upload'); }); // 文件上传接口 server.post('/upload', upload.single('file'), (req, res) => { res.redirect("/"); });
对应upload.ejs上传页面代码如下:
<form action="/upload" method = "post" enctype="multipart/form-data"> <div class="custom-file mb-3"> <input type="file" name="file" id="file" class="custom-file-input"> </div> <input type="submit" value="Submit" class="btn btn-primary btn-block"> </form>
目前访问localhost:3000/list可正常查看已上传文件列表,list.ejs现有代码如下:
<% if(files){ %> <% files.forEach(function(file) { %> <div class="card card-body mb-3"> <%= file.filename %> <a href= <%=mongoURI%> download=file>Download</a> <form action= "<%= file._id %>?_method=DELETE" method="get"> <button class="delete">DELETE</button> </form> <form action="<%= file._id %>?_method=DOWNLOAD" method="get"> <button class="download">DOWNLOAD</button> </form> </div> <% }) %> <% } else { %> <p>No files to show</p> <% } %>
当前list.ejs可正确读取文件名,需要实现文件下载与删除功能。由于前端不能直接与数据库交互,相关逻辑需要在server.js中编写,参考上传逻辑开发未成功。
问题修复与功能实现
原有代码问题点
- 重复声明了Mongo连接变量
conn,GridFS初始化时未指定和存储引擎对齐的集合名 - 列表页下载链接直接填写MongoDB连接地址无法生效,删除/下载请求路径不完整
methodOverride默认仅拦截POST请求,现有删除表单用GET方法无法触发DELETE方法的路由
第一步:在server.js中补充下载、删除接口
在现有上传接口后追加以下代码:
// 文件列表接口(已实现可忽略,注意要将查询到的文件列表传入模板) server.get('/list', (req, res) => { gfs.files.find().toArray((err, files) => { if(!files || files.length === 0) return res.render('list', {files: false}); return res.render('list', {files: files}); }) }) // 文件下载接口 server.get('/files/:id/download', (req, res) => { gfs.files.findOne({_id: mongoose.Types.ObjectId(req.params.id)}, (err, file) => { if(!file || file.length === 0) return res.status(404).send('文件不存在'); // 设置响应头,触发浏览器下载行为 res.set('Content-Type', file.contentType); res.set('Content-Disposition', `attachment; filename="${file.filename}"`); // 读取GridFS文件流写入响应 const readStream = gfs.createReadStream(file.filename); readStream.pipe(res); }) }) // 文件删除接口 server.delete('/files/:id', (req, res) => { gfs.remove({_id: mongoose.Types.ObjectId(req.params.id), root: 'fs'}, (err, gridStore) => { if(err) return res.status(404).send('删除失败'); res.redirect('/list'); }) })
第二步:修改list.ejs模板的请求地址
把原有卡片内的下载、删除按钮部分替换为以下代码:
<div class="card card-body mb-3"> <%= file.filename %> <a href="/files/<%= file._id %>/download" class="btn btn-success">下载文件</a> <form action="/files/<%= file._id %>?_method=DELETE" method="post"> <button class="btn btn-danger delete" type="submit">删除文件</button> </form> </div>
注意:删除表单必须用post方法,否则methodOverride不会识别_method参数指定的DELETE请求
内容的提问来源于stack exchange,提问作者OneBite
相关产品推荐
相关产品推荐

