likes
comments
collection
share

【Node.js实战】文件上传-Express 搭建博客(三)

作者站长头像
站长
· 阅读数 19

前言

哈喽,大家好,这里是胖芙,咱这系列的文章主打的就是一个通俗易懂。这个系列的文章会从零开始教你怎么使用 Express.js 去搭建一个能用的博客系统,是真的从零,手把手的那种!!!让你打通 JS 前后端任督二脉!

书接上文

上一篇文章中我们完成了登录和注册功能,已经可以注册新用户并且在登陆的时候下发用户信息和请求凭证 token。

接下来我们这里要实现的就是文件上传和接口鉴权的功能;

文件上传:比如发表博客的时候我们可以上传一个封面图,这时候就需要有一个文件上传的接口,文件上传完成之后会返回一个文件存储的地址,获取这个文件的时候可以直接访问对应的地址即可。上传文件有两种存储方式,一种是存储在服务端本地,另一种是存储到 OSS,我们这里会把两种方式都实现一下。

接口鉴权:除了登录和注册的接口以外,其它所有的接口都需要进行鉴权校验,比如上传文件就必须携带 token,也就是需要先进行登录

废话不多说,我们先来实现文件上传功能,直接来吧~

定义路由和控制器

上传文件我们首先需要安装好 multer 这个依赖,在第一章的时候我们已经搞好了,那么接下来再看一下我们之前第一章中定义好了的路由和控制器。首先是入口文件定义好路由以及添加本地静态文件访问,根目录同时创建一个 tempFiles 目录用于存储上传的文件

// 根目录/app.js

... 其它代码请查看第一章
const fileRoutes = require("./routes/file");

// 提供静态文件访问
app.use("/tempFiles", express.static(path.join(__dirname, "tempFiles")));

// 路由
app.use("/upload", fileRoutes);

... 其它代码请查看第一章

接下来定义文件上传路由

// 根目录/routes/file.js
const express = require("express");
const router = express.Router();
const fileController = require("../controllers/fileController");

// 目前只有一个上传文件接口
router.post("/file", fileController.uploadFile);

module.exports = router;

定义文件上传控制器

// 根目录/controllers/fileController.js

async function uploadFile(req, res) {
  // todo
}

module.exports = { uploadFile };

定义文件上传中间件

中间件本质上就是一个处理函数,可以在请求之前做一些相关的操作, 当一个请求到达express 的服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理。

中间件的种类:全局中间件、路由中间件、全局错误处理中间件、第三方中间件等

我们这里要定义的文件上传中间件则属于路由级别中间件,就是在上传文件的接口请求之前做一些预处理,其实就是请求的时候先获得文件上传的路径,然后再在文件上传控制器中把文件上传的路径给返回客户端。

上传到本地

首先来定义一个文件上传中间件

// 根目录/middleware/fileMiddleware.js

const multer = require("multer");
const fs = require("fs");
const path = require("path");

// 文件上传配置
const storage = multer.diskStorage({
  // 上传的目标地址,这里我们就上传到本地根目录的 tempFiles 目录
  destination: (req, file, cb) => {
    const tempFolderPath = path.join(__dirname, "../tempFiles");
    // 如果目录不存在则创建
    if (!fs.existsSync(tempFolderPath)) {
      fs.mkdirSync(tempFolderPath);
    }
    cb(null, tempFolderPath);
  },
  // 定义上传的文件名
  filename: (req, file, cb) => {
    const ext = path.extname(file.originalname);
    const filename = `${Date.now()}_${Math.floor(Math.random() * 10000)}${ext}`;
    cb(null, filename);
  },
});

// 文件上传中间件
const upload = multer({ storage });
const uploadMiddleware = upload.single("file");

module.exports = uploadMiddleware

定义好了中间件之后我们怎么使用呢?

直接在上传文件的路由中添加就好了

// 根目录/routes/file.js

const express = require("express");
const router = express.Router();
const fileController = require("../controllers/fileController");
// 导入
const uploadMiddleware = require("../middleware/fileMiddleware");

// 添加文件上传处理中间件
router.post("/file", uploadMiddleware, fileController.uploadFile);

module.exports = router;

接下来我们再来完善一下文件上传控制器里的逻辑

// 根目录/controllers/fileController.js

async function uploadFile(req, res) {
  const { file } = req;

  if (!file) {
    return res.status(400).json({ message: "No file provided" });
  }

  // 返回图片地址
  res.json({ msg: "File uploaded successfully", filePath: file.path });
}

module.exports = { uploadFile };

有了中间件之后,我们上传文件控制器的逻辑就这么简单

接下来我们来做一下测试哈,在第二章中我们已经把项目启动起来了,如果不知道可以去看前面的章节

现在我们打开 Postman,然后选择 POST 接口 Body 中的 form-data,把 key 改成 file,然后选择一个本地的文件进行上传

【Node.js实战】文件上传-Express 搭建博客(三)

最后我们可以看到文件成功上传到服务器根目录的 tempFiles 目录下了,一般在实际开发中,我们不会把文件的绝对地址给返回,需要进行额外的处理,比如返回 服务器地址/tempFiles/文件名.png 这样的一个路径,如 localhost:3000/tempFiles/puff.png,这样用户直接访问这个地址就能够看到我们的图片了

【Node.js实战】文件上传-Express 搭建博客(三)

说完了本地上传的逻辑,接下来我们再看下如何把文件上传到阿里云 OSS 上

上传文件到 OSS

为什么要把文件上传到 OSS ? 首先是降低我们自己的维护成本,第二是可以提高服务器的性能,第三是大厂商的服务更大程度上来说会比我们自己搭建的文件服务要稳定得多,成本也会相对比自己搭建服务要低

说完了 OSS 的优点,那么接下来我们就看下如何把文件上传到 OSS 上吧,这里我们使用阿里云的对象存储 OSS

首先要登录阿里云官网,然后找到对象存储 OSS,进入到管理控制台

【Node.js实战】文件上传-Express 搭建博客(三)

创建一个 Bucket

【Node.js实战】文件上传-Express 搭建博客(三)

按需填写信息即可

【Node.js实战】文件上传-Express 搭建博客(三)

然后此时把这个 EndPoint 节点信息的前半部分 oss-cn-hangzhou 先记录下来,它叫做 Region (地区),一会需要用

【Node.js实战】文件上传-Express 搭建博客(三)

接下来我们就去拿一下我们的访问 AccessKey

【Node.js实战】文件上传-Express 搭建博客(三)

创建一个账户

【Node.js实战】文件上传-Express 搭建博客(三) 记得把 OpenAPI 调用访问勾选上

【Node.js实战】文件上传-Express 搭建博客(三)

最后我们就得到访问 OSS 的 key 和 secret 了,先记录下来,一会也需要用到

【Node.js实战】文件上传-Express 搭建博客(三)

接下来我们得授权这个子账号访问 OSS 的权限,接下来就可以进行 OSS 的访问了

【Node.js实战】文件上传-Express 搭建博客(三)

最后得到了 Bucket、 Region、AccessKey、AccessKey Secret 这 4 个信息之后,我们就可以编写代码了,通过 API 的方式进行调用将文件上传

OSS 上传代码编写

先安装一下依赖 npm install multer-aliyun-oss

然后添加一个 ossMiddleware.js 中间件文件到 根目录/middleware 文件夹下

// 根目录/middleware/ossMiddleware.js

const multer = require("multer");
const MAO = require("multer-aliyun-oss");

const upload = multer({
  storage: MAO({
    config: {
      region: "oss-cn-hangzhou",
      accessKeyId: "刚刚申请的ID",
      accessKeySecret: "刚刚申请的密钥",
      bucket: "express-blog",
    },
  }),
  // oss 中存放文件的文件夹
  destination: "public/images",
});

const ossMiddleware = upload.single('file')

module.exports = ossMiddleware;

然后我们到路由中引入一下

// 根目录/routes/file.js

const express = require("express");
const router = express.Router();
const fileController = require("../controllers/fileController");
// const uploadMiddleware = require("../middleware/fileMiddleware");
const ossUploadMiddleware = require("../middleware/ossMiddleware");

// 修改为 ossUploadMiddleware
router.post("/file", ossUploadMiddleware, fileController.uploadFile);

module.exports = router;

最后还要修改一下控制器中的返回字段

// 根目录/controllers/fileController.js

async function uploadFile(req, res) {
  const { file } = req;

  if (!file) {
    return res.status(400).json({ message: "No file provided" });
  }

  // 把 file.path 改成 file.url
  res.json({ msg: "File uploaded successfully", filePath: file.url });
}

module.exports = { uploadFile };

好了,到这里上传文件到 OSS 就大功告成了~

让我们来 Postman 测试一下,可以看到成功的返回了 OSS 存储文件的路径~~

【Node.js实战】文件上传-Express 搭建博客(三)

不过由于刚刚我们把 Bucket 的访问权限改成了私有,所以目前这个 filePath URL 是访问不了的~,如果想要开放到所有人都可以访问,去控制台修改一下 Bucket 的权限为 公共读 即可,不过这时候就要注意一下流量了,因为 OSS 是要收钱的,不过并不贵~

好了,到这里我们的上传文件就大功告成了

接下来我们来看看如何给上传文件的接口增加鉴权,也就是说用户必须先进行登录才能进行访问这个接口

定义鉴权中间件

那么有了上传文件的接口之后,那肯定是需要携带登录凭证过来的,所以我们就添加一个鉴权的中间件用于给非登录和注册以外的接口都加上鉴权机制

// 根目录/middleware/authMiddleware.js

const jwt = require("jsonwebtoken");

// 鉴权中间件
function authMiddleware(req, res, next) {
  const authHeader = req.headers["authorization"];
  // 从 Authorization 头部解析 token,可以使用 Axios 的拦截器全局携带上该值
  // token 一般前面都是带 Bearer 的,然后后面接一个空格,接下来才是我们自己定义的 token 内容
  const token = authHeader && authHeader.split(" ")[1];

  if (!token) {
    return res.status(401).json({ error: "Unauthorized" });
  }

  // 验证 token,这个 token 密钥需要和我们登录时定义的那个一一对应上才行
  jwt.verify(token, "xxx-your-secret-key", (err, user) => {
    if (err) {
      return res.status(403).json({ error: "Invalid token" });
    }

    // 将用户信息存储到请求对象中,方便后续处理
    req.user = user;

    // 需要调用一下 next 放行到下一个处理中间件
    next();
  });
}

module.exports = authMiddleware

鉴权中间件添加到全局

完整的定义可以去看看前面的章节哈,这里我们主要就看增加的 app.use(authMiddleware) 这一行代码

... 其它导入的内容
const authMiddleware = require("./middleware/authMiddleware");

const app = express();

// 中间件
...
// 提供静态文件访问
...

// 路由
app.use("/auth", authRoutes);

// 除了登录和注册接口之外,其它所有的接口都会走鉴权中间件
app.use(authMiddleware);
app.use("/blogs", blogRoutes);
app.use("/tags", tagRoutes);
app.use("/upload", fileRoutes);

// 数据库同步
...
// 启动服务器
...

这时候我们可以试一下不携带 token 的情况上传文件,可以看到正确的返回了未授权

【Node.js实战】文件上传-Express 搭建博客(三)

然后我们这时候进行登录,拿到 token,再携带 token 来发送请求试试,把这个 token 给复制下来

【Node.js实战】文件上传-Express 搭建博客(三)

然后携带着 token 再发送一下请求,这一次可以发送成功了

【Node.js实战】文件上传-Express 搭建博客(三) 好了,这一章的内容到这里就基本结束了~~

小结

这一章中我们完成的内容有:

  • 上传文件到本地
  • 上传文件到OSS
  • 完成接口鉴权

下次遇到这样类似的场景再也不怕了~

好了,下一章节我们就要进入到 tag 和 blog 两套接口的实现,也是该系列的最后一章

想要及时收到推送,欢迎关注公众号《泡芙学前端》,同步更新文章中...