持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情
Reactjs + Nodejs + Express + Mongodb 搭建[图片上传/预览]前后端项目
概述
图片上传功能,在日常的项目开发中是很常见的功能,今天我们就来说说使用 Reactjs + Nodejs + Express + Mongodb 搭建前后端图片上传的例子
我们先看看完成后的效果
跟随本示例学习,你也可以搭建出来。
后端部分
实现的功能:
- 图片上传功能
- 图片上传显示进度条功能
- 图片预览功能
- 图片列表功能
- 图片下载功能
后端使用的技术/数据库:
- Nodejs
- Express
- Multer
- Mongodb
- Multer-gridfs-storage
- Cors
后端部分我们使用 Nodejs + Express + Multer + Mongodb 来搭建图片上传的项目,配合前端 Reactjs + Axios 来共同实现图片上传的前后端项目。
后端项目目录结构
├── README.md
├── node_modules
├── package-lock.json
├── package.json
└── src
├── config
│ └── db.js
├── controllers
│ ├── home.js
│ └── upload.js
├── middleware
│ └── upload.js
├── routes
│ └── index.js
├── server.js
└── views
└── index.html
- config/db.js:包括
MongoDB和Multer的配置(url、数据库、图像存储桶)。 - routes/index.js:定义从视图调用的端点的路由,使用控制器来处理请求。
- controllers:
home.js返回
views/index.htmlupload.js处理上传、存储、显示和下载图像 - middleware/upload.js:初始化
Multer GridFs存储引擎(包括MongoDB)并定义中间件函数。 - server.js:初始化路由,配置 CORS,入口文件
后端项目我们提供以下几个API
- 文件上传接口
- 文件列表获取接口
- 使用
url下载文件接口
我们可以使用 postman 工具先看下接口的情况,如下图
文件上传接口
获取文件列表接口
打开数据库连接工具,可以看到,数据库里,已经有上传的文件了
创建项目 配置模块
我们先使用命令 mkdir 创建一个空文件夹,然后 cd 到文件夹里面
这个文件夹就是我们的项目文件夹
mkdir nodejs-express-upload-files
cd nodejs-express-upload-files
接着使用命令
npm init
初始化项目,接着安装项目需要的依赖包, 输入如下命令
npm install express cors multer multer-gridfs-storage mongodb
package.js 文件
{
"name": "nodejs-express-upload-files",
"version": "1.0.0",
"description": "Node.js upload multiple files/images to MongoDB",
"main": "src/server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"node",
"upload",
"multiple",
"files",
"images",
"mongodb"
],
"license": "ISC",
"dependencies": {
"cors": "^2.8.5",
"express": "^4.17.1",
"mongodb": "^4.1.3",
"multer": "^1.4.3",
"multer-gridfs-storage": "^5.0.2"
}
}
配置 MongoDB 数据库
config/db.js
module.exports = {
url: "mongodb://localhost:27017/",
database: "files_db",
imgBucket: "photos",
};
配置图片上传存储的中间件
middleware/upload.js
const util = require("util");
const multer = require("multer");
const { GridFsStorage } = require("multer-gridfs-storage");
const dbConfig = require("../config/db");
var storage = new GridFsStorage({
url: dbConfig.url + dbConfig.database,
options: { useNewUrlParser: true, useUnifiedTopology: true },
file: (req, file) => {
const match = ["image/png", "image/jpeg"];
if (match.indexOf(file.mimetype) === -1) {
const filename = file.originalname;
return filename;
}
return {
bucketName: dbConfig.imgBucket,
filename: file.originalname
};
}
});
var uploadFiles = multer({ storage: storage }).array("file", 10);
var uploadFilesMiddleware = util.promisify(uploadFiles);
module.exports = uploadFilesMiddleware;
这里我们定义一个 storage 的配置对象 GridFsStorage
-
url: 必须是指向
MongoDB数据库的标准MongoDB连接字符串。multer-gridfs-storage模块将自动为您创建一个mongodb连接。 -
options: 自定义如何建立连接
-
file: 这是控制数据库中文件存储的功能。该函数的返回值是一个具有以下属性的对象:filename, metadata, chunkSize, bucketName, contentType... 我们还检查文件是否为图像file.mimetype。bucketName表示文件将存储在photos.chunks和photos.files集合中。
-
接下来我们使用multer模块来初始化中间件util.promisify()并使导出的中间件对象可以与async-await.
-
single()带参数的函数是input标签的名称
创建文件上传的控制器
controllers/upload.js
这个文件主要用于图片上传,我们创建一个名 upload 函数,并将这个函数导出去
- 我们使用 文件上传中间件函数处理上传的文件
- 使用
Multer捕获相关错误 - 返回响应
文件列表数据获取和下载
getListFiles函数主要是获取photos.files,返回url, name, iddownload(): 接收文件id作为输入参数,从 mongodb 内置打开下载流GridFSBucket,然后response.write(chunk)API 将文件传输到客户端。
const upload = require("../middleware/upload");
const dbConfig = require("../config/db");
const MongoClient = require("mongodb").MongoClient;
const GridFSBucket = require("mongodb").GridFSBucket;
const url = dbConfig.url;
const baseUrl = "http://localhost:8080/files/";
const mongoClient = new MongoClient(url);
const uploadFiles = async (req, res) => {
try {
await upload(req, res);
console.log(req.files);
if (req.files.length <= 0) {
return res
.status(400)
.send({ message: "You must select at least 1 file." });
}
return res.status(200).send({
message: "文件上传成功",
});
// console.log(req.file);
// if (req.file == undefined) {
// return res.send({
// message: "You must select a file.",
// });
// }
// return res.send({
// message: "File has been uploaded.",
// });
} catch (error) {
console.log(error);
if (error.code === "LIMIT_UNEXPECTED_FILE") {
return res.status(400).send({
message: "Too many files to upload.",
});
}
return res.status(500).send({
message: `Error when trying upload many files: ${error}`,
});
// return res.send({
// message: "Error when trying upload image: ${error}",
// });
}
};
const getListFiles = async (req, res) => {
try {
await mongoClient.connect();
const database = mongoClient.db(dbConfig.database);
const images = database.collection(dbConfig.imgBucket + ".files");
let fileInfos = [];
if ((await images.estimatedDocumentCount()) === 0) {
fileInfos = []
}
let cursor = images.find({})
await cursor.forEach((doc) => {
fileInfos.push({
id: doc._id,
name: doc.filename,
url: baseUrl + doc.filename,
});
});
return res.status(200).send(fileInfos);
} catch (error) {
return res.status(500).send({
message: error.message,
});
}
};
const download = async (req, res) => {
try {
await mongoClient.connect();
const database = mongoClient.db(dbConfig.database);
const bucket = new GridFSBucket(database, {
bucketName: dbConfig.imgBucket,
});
let downloadStream = bucket.openDownloadStreamByName(req.params.name);
downloadStream.on("data", function (data) {
return res.status(200).write(data);
});
downloadStream.on("error", function (err) {
return res.status(404).send({ message: "Cannot download the Image!" });
});
downloadStream.on("end", () => {
return res.end();
});
} catch (error) {
return res.status(500).send({
message: error.message,
});
}
};
module.exports = {
uploadFiles,
getListFiles,
download,
};
定义路由
在 routes 文件夹中,使用 Express Router 在 index.js 中定义路由
const express = require("express");
const router = express.Router();
const homeController = require("../controllers/home");
const uploadController = require("../controllers/upload");
let routes = app => {
router.post("/upload", uploadController.uploadFiles);
router.get("/files", uploadController.getListFiles);
router.get("/files/:name", uploadController.download);
return app.use("/", router);
};
module.exports = routes;
- POST"/upload"调用
uploadFiles控制器的功能。 - 获取/files图像列表。
- GET/files/:name下载带有文件名的图像。
创建 Express 服务器
server.js
const cors = require("cors");
const express = require("express");
const app = express();
const initRoutes = require("./routes");
var corsOptions = {
origin: "http://localhost:8081"
};
app.use(cors(corsOptions));
app.use(express.urlencoded({ extended: true }));
initRoutes(app);
let port = 8080;
app.listen(port, () => {
console.log(`Running at localhost:${port}`);
});
这里我们导入了 express 和 cors,
Express用于构建 Rest apiCors提供Express中间件以启用具有各种选项的CORS。
创建一个 Express 应用程序,然后使用方法添加 Cors 中间件
在端口 8080 上侦听传入请求。
运行项目并测试
在项目根目录下在终端中输入命令 node src/server.js, 控制台显示
Running at localhost:8080
使用 postman 工具测试,ok 项目正常运行
文件上传接口
获取文件列表接口
打开数据库连接工具,可以看到,数据库里,已经有上传的文件了
联调
我们先启动后端项目 node src/server.js, 接着再启动前端项目 npm start,
测试上传,获取等接口,一切正常。
到这里整个前后端「上传图片」功能示例就算完成了。