这篇“如何使用Node和MongoDB搭建一个图床或网盘”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“如何使用Node和MongoDB搭建一个图床或网盘”文章吧。
1 文章起源
这个项目比较简单,看完的同学就可以很快的搭建一个小网盘或者图床了。
2 起手式
2.1 概念
首先我们得先去了解一下Mongodb
的文件储存(GridFS)是啥,因为我们都是基于 GridFS
来进行文件储存
2.2 我们需要什么
了解了大概概念后就可以着手安装我们必须的插件了
express
(这是啥不用我多说)body-parser
(Node解析body的中间件)ejs
(模板引擎,快速开发就不搞前后端分离了,有兴趣的小伙伴可以用Vue/React来搭建小网盘)gridfs-stream
(轻松地与MongoDB GridFS之间传输文件。)method-override
(我们用form表单简单上传,因为form表单不支持put/delete请求方式,所以把它安排上了,小伙伴可自行使用Ajax,就不需要这么麻烦了)mongoose
(用于连接mongodb必不可少的插件)multer
(Multer是用于处理多部分/表单数据的node.js中间件,主要用于上传文件。它被编写在busboy之上,以实现最大效率。)multer-gridfs-storage
(Multer的GridFS存储引擎可将上传的文件直接存储到MongoDb。)nodemon
(热更新)
(推荐教程:Node入门)
以上就是我们需要准备的东西了
npm install express body-parser ejs gridfs-stream method-override mongoose multer multer-gridfs-storage// oryarn add express body-parser ejs gridfs-stream method-override mongoose multer multer-gridfs-storage
2.3 初始化一个项目
// 可自行补充信息// npm init
然后在根目录新建一个入口文件app.js
,和页面 views/index.ejs
3 现在项目开始了
3.1 先将基础部分完事
将我们安装的包引入,再跑跑看看
const express = require('express')const path = require('path')const crypto = require('crypto')const mongoose = require('mongoose')const multer = require('multer')const GridFsStorage = require('multer-gridfs-storage')const GridFsStream = require('gridfs-stream')const methodOverride = require('method-override')const bodyParser = require('body-parser')
const app = express()app.set('view engine', 'ejs') // 设置模板引擎app.use(bodyParser.json()) app.use(methodOverride('_method'))app.get('/', (req, res) => { res.render('index') })})const port = 5000app.listen(port, () => { console.log(`App listering on port ${port}`)})
一般来说启动了app.js
的话我们在浏览器访问 http://localhost:5000
就能看到 views/index.ejs
中的界面了,如果没有,自行查看控制台是否报错
3.2 连接我们的Mongodb数据库
我这边用的本地mongodb
数据库,线上也是一样的,我们可以用NoSQL manager for mongdb
来查看我们数据库里面的数据,我们新建一个新的集合,我这边叫 grid_uploads
。所以连接的话也是连接这个集合
// 数据库的链接const mongoURL = 'mongodb://localhost:27017/grid_uploads'
const connect = mongoose.createConnection(mongoURL, { useNewUrlParser: true, useUnifiedTopology: true})
3.3 美化一下界面(views/index.ejs)
作为一个小两年的前端工程师,已经练就了像素眼了,我们肯定不能把界面做的辣么丑对吧,眼睛过不去啊,所以我们简单的用bootstrap4
来做个界面好了
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>文件上传</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.0/dist/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous"> <style> img { width: 100%; } </style></head><body> <div class="container"> <div class="row"> <div class="col-md-6 m-auto"> <h3 class="text-center display-4 my-4">Mongo文件上传</h3> <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"> <label for="file" class="custom-file-label">选择文件</label> </div> <input class="btn btn-primary btn-block" type="submit" value="提交"> </form> <hr> </div> </div> </div></body><script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.0/dist/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script></html>
3.4 做一些必须的处理
// 定义gfs变量,后续我们进行数据库文件操作的时候可不能少let gfs;connect.once('open', () => {// 监听数据库开启,通过 gridfs-stream 中间件和数据库进行文件的出入控制gfs = GridFsStream(connect.db, mongoose.mongo)gfs.collection('upload')// 它会在我们数据库中建立 upload.files(记录文件信息) upload.chunks(存储文件块)})
// 使用 multer-gridfs-storage Multer 中间件来讲我们上传的附件直接存储到MongoDbconst storage = new GridFsStorage({ url: mongoURL, file: (req, file) => { return new Promise((resolve, reject) => { // 下面注释部分是给文件进行重命名的,如果想要原文件名称可以自行使用 file.originalname 返回, // 建议有时间的小伙伴存储两个文档,一个记录原文件名,一个记录加密文件名,然后返回到页面的时候可以将中文名返回去 // crypto.randomBytes(16, (err, buf) => { // if (err) { // return reject(err) // } // const filename = buf.toString('hex') + path.extname(file.originalname) // const fileinfo = { // filename, // bucketName: 'upload' // } // resolve(fileinfo) // }) const fileinfo = { filename: new Date() + '-' + file.originalname, bucketName: 'upload' } resolve(fileinfo) }) }})const upload = multer({ storage })
3.5 写我们上传第一个文件的接口
app.post('/upload', upload.single('file'), (req, res) => {res.redirect('/')})
看起来简简单单,请记着这么几件事
在
views/index.ejs
中 (input type=file
指定的name
得和接口的upload.single
('file') 一样上传完文件我们重定向回我们的首页 此时我们就可以在
NoSql
看到我们的两个文档有数据了
3.6 获取我们所有的文件信息
获取我们所有的文件
app.get('/files', (req, res) => {// 通过查找返回一个数组对象回去gfs.files.find().toArray((err, files) => {if (!files || files.length === 0) {return res.status(404).json({err: '文件不存在!'})}return res.json(files)})})
我们可以进行一些美化操作,比如我们可以将上传是图片的,返回到界面的话以图片显示,其他则以 a
标签的格式显示(可点击下载),所以我们可以将 views/index.ejs
的界面进行美化改造(ejs
语法用起来确实蛮麻烦的),进行重新排版以及添加删除按钮
<!DOCTYPE html><html lang="en">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>文件上传</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.0/dist/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous"> <style> img { width: 100%; } </style></head><body> <div class="container"> <div class="row"> <div class="col-md-6 m-auto"> <h3 class="text-center display-4 my-4">Mongo文件上传</h3> <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"> <label for="file" class="custom-file-label">选择文件</label> </div> <input class="btn btn-primary btn-block" type="submit" value="提交"> </form> <hr> </div> </div> <div class="row"> <% if(files){ %> <% files.forEach(function(file){ %> <div class="col-sm card card-body m-3 col-md-2"> <% if(file.isImage){ %> <img src="image/<%= file.filename %>" /> <% } else { %> <a href="download/<%= file.filename %>"><%= file.filename %></a> <%}%> <form action="/files/<%= file._id%>?_method=DELETE" method="POST"> <button class="btn btn-danger btn-block mt-4">删除</button> </form> </div> <% }) %> <% }else { %> <p class="card card-body text-center display-4 my-4">文件不存在</p> <% } %> </div> </div></body><script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.0/dist/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script></html>
要将 ejs
中的files
变量获取到我们应该重写一下 get('/')
接口,使其在访问localhost:5000
的时候先去读取一下数据库文件信息并输出到页面中去
app.get('/', (req, res) => {gfs.files.find().toArray((err, files) => {if (!files || files.length === 0) {res.render('index', { files: false })return}files.map(file => {// 如果是以下图片类型我们就在前端展示出来,其余一律按附件处理,通过 isImage 来区分图片和非图片const imageType = ['image/png', 'image/jpg', 'image/gif', 'image/jpeg']if (imageType.includes(file.contentType)) {file.isImage = true} else {file.isImage = false}})res.render('index', { files: files })})})
3.7 单个文件下载
在这里我们通过a标签访问 /download/:filename
接口,filename
是文件名,当然可以用其他的比如_id
,当查找到有该附件的时候就将它合并成可读留,通过管道返回,这样在前端界面上点击文件标题就可以直接下载了
app.get('/download/:filename', (req, res) => {gfs.files.findOne({ filename: req.params.filename }, (err, file) => {if (!file) {return res.status(404).json({err: '文件不存在!'})}const readstream = gfs.createReadStream(file.filename)readstream.pipe(res)})})
3.8 单个文件删除
在这里我们通过a
标签访问 /files/:id
接口,id
对应,点击删除按钮,就直接删除了,并重定向到首页
app.delete('/files/:id', (req, res) => {gfs.remove({ _id: req.params.id, root: 'upload' }, (err) => {if (err) {return res.status(404).json({err: '删除的文件不存在!'})}res.redirect('/')})})
由于我们一直用form
做请求,但是form
表单没有delete
请求方式,所以我们用到了method-override
插件,当然要是用Ajax
就没关系了,我们项目毕竟速成嘛,主要看效果和过程。
以上就是关于“如何使用Node和MongoDB搭建一个图床或网盘”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注编程网行业资讯频道。