# vue-api **Repository Path**: yanjiushen/vue-api ## Basic Information - **Project Name**: vue-api - **Description**: 使用Express开发接口 包括双Token无感刷新、文件上传 Mysql - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-02-02 - **Last Updated**: 2024-02-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: Express, Nodejs, token, 文件上传, 接口 ## README # VUE-API ## 插件 > nodemon: 文件改动时重启服务器 > express: 框架 > multer: 用于处理 multipart/form-data 类型的表单数据 > orm: 提供数据库操作 > mysql: 连接 MySql 数据库 > node-uuid: 生成 UUID > jsonwebtoken: 用于生成 JWT 字符串 > express-jwt: 将 jwt 字符串进行解析还原为 JSON 对象 ## multer 文件上传 用于处理 `multipart/form-data` 类型的表单数据,它主要用于上传文件。 **注意**: `Multer` 不会处理任何非 `multipart/form-data` 类型的表单数据。 `Multer` 会添加一个 `body` 对象 以及 `file` 或 `files` 对象 到 `express` 的 `request` 对象中。 `body` 对象包含表单的文本域信息,`file` 或 `files` 对象包含对象表单上传的文件信息。 ### 基本使用 ```js const express = require("express"); const multer = require("multer"); const upload = multer({ dest: "uploads/" }); const app = express(); app.post("/profile", upload.single("avatar"), function (req, res, next) { // req.file 是 `avatar` 文件的信息 // req.body 将具有文本域数据,如果存在的话 }); app.post( "/photos/upload", upload.array("photos", 12), function (req, res, next) { // req.files 是 `photos` 文件数组的信息 // req.body 将具有文本域数据,如果存在的话 } ); const cpUpload = upload.fields([ { name: "avatar", maxCount: 1 }, { name: "gallery", maxCount: 8 }, ]); app.post("/cool-profile", cpUpload, function (req, res, next) { // req.files 是一个对象 (String -> Array) 键是文件名,值是文件数组 // // 例如: // req.files['avatar'][0] -> File // req.files['gallery'] -> Array // // req.body 将具有文本域数据,如果存在的话 }); ``` 如果你需要处理一个只有文本域的表单,你应当使用 `.none()`: ```js const express = require("express"); const app = express(); const multer = require("multer"); const upload = multer(); app.post("/profile", upload.none(), function (req, res, next) { // req.body 包含文本域 }); ``` ### 文件信息 | Key | Description | Note | | ------------ | ----------------------------- | ------------- | | fieldname | Field name | 由表单指定 | | originalname | 用户计算机上的文件的名称 | | | encoding | 文件编码 | | | mimetype | 文件的 MIME 类型 | | | size | 文件大小(字节单位) | | | destination | 保存路径 DiskStorage | | | filename | 保存在 destination 中的文件名 | DiskStorage | | path | 已上传文件的完整路径 | DiskStorage | | buffer | 一个存放了整个文件的 Buffer | MemoryStorage | ### API #### multer(opts) Multer 接受一个 `options` 对象,其中最基本的是 `dest` 属性,这将告诉 Multer 将上传文件保存在哪。如果你省略 `options` 对象,这些文件将保存在内存中,永远不会写入磁盘。 为了避免命名冲突,Multer 会修改上传的文件名。这个重命名功能可以根据您的需要定制。 以下是可以传递给 Multer 的选项。 | Key | Description | | --------------- | ---------------------------------- | | dest or storage | 在哪里存储文件 | | fileFilter | 文件过滤器,控制哪些文件可以被接受 | | limits | 限制上传的数据 | | preservePath | 保存包含文件名的完整文件路径 | ##### dest or storage 存储 ###### dest ```js const upload = multer({ dest: "uploads/" }); ``` ###### 磁盘存储引擎 (DiskStorage) ```js const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, "/tmp/my-uploads"); }, filename: function (req, file, cb) { cb(null, file.fieldname + "-" + Date.now()); }, }); const upload = multer({ storage: storage }); ``` `destination` 是用来确定上传的文件应该存储在哪个文件夹中。也可以提供一个 string (例如 `'/tmp/uploads'`)。如果没有设置 `destination`,则使用操作系统默认的临时文件夹。 **注意**: 如果你提供的 `destination` 是一个函数,你需要负责创建文件夹。当提供一个字符串,`multer` 将确保这个文件夹是你创建的。 `filename` 用于确定文件夹中的文件名的确定。 如果没有设置 `filename`,每个文件将设置为一个随机文件名,并且是没有扩展名的。 **注意**: Multer 不会为你添加任何扩展名,你的程序应该返回一个完整的文件名。 ###### 内存存储引擎 (MemoryStorage) 内存存储引擎将文件存储在内存中的 Buffer 对象,它没有任何选项。 ```js const storage = multer.memoryStorage(); const upload = multer({ storage: storage }); ``` 当使用内存存储引擎,文件信息将包含一个 buffer 字段,里面包含了整个文件数据。 ##### limits 文件限制 一个对象,指定一些数据大小的限制。 | Key | Description | Default | | ------------- | -------------------------------------------------------- | --------- | | fieldNameSize | field 名字最大长度 | 100 bytes | | fieldSize | field 值的最大长度 | 1MB | | fields | 非文件 field 的最大数量 | 无限 | | fileSize | 在 multipart 表单中,文件最大长度 (字节单位) | 无限 | | files | 在 multipart 表单中,文件最大数量 | 无限 | | parts | 在 multipart 表单中,part 传输的最大数量(fields + files) | 无限 | | headerPairs | 在 multipart 表单中,键值对最大组数 | 2000 | ##### fileFilter 文件过滤 设置一个函数来控制什么文件可以上传以及什么文件应该跳过 ```js function fileFilter(req, file, cb) { // 这个函数应该调用 `cb` 用boolean值来 // 指示是否应接受该文件 // 拒绝这个文件,使用`false`,像这样: cb(null, false); // 接受这个文件,使用`true`,像这样: cb(null, true); // 如果有问题,你可以总是这样发送一个错误: cb(new Error("I don't have a clue!")); } ``` #### .single(fieldname) 接受一个以 `fieldname` 命名的文件。这个文件的信息保存在 `req.file` ```js const multer = require("multer"); const upload = multer({ dest: "./public/data/uploads/" }); app.post("/stats", upload.single("uploaded_file"), function (req, res) { // req.file is the name of your file in the form above, here 'uploaded_file' // req.body will hold the text fields, if there were any console.log(req.file, req.body); }); ``` #### .array(fieldname[, maxCount]) 接受一个以 `fieldname` 命名的文件数组。可以配置 `maxCount` 来限制上传的最大数量。这些文件的信息保存在 `req.files`。 ```js const multer = require("multer"); const upload = multer({ dest: "./public/data/uploads/" }); app.post( "/photos/upload", upload.array("photos", 12), function (req, res, next) { // req.files 是 `photos` 文件数组的信息 // req.body 将具有文本域数据,如果存在的话 } ); ``` #### .fields(fields) 接受指定 `fields` 的混合文件。这些文件的信息保存在 `req.files`。 #### .fields 应该是一个对象数组,应该具有 `name` 和可选的 `maxCount` 属性。 ```js [ { name: "avatar", maxCount: 1 }, { name: "gallery", maxCount: 8 }, ]; ``` #### .none() 只接受文本域。如果任何文件上传到这个模式,将发生 "LIMIT_UNEXPECTED_FILE" 错误。这和 upload.fields([]) 的效果一样。 #### .any() 接受一切上传的文件。文件数组将保存在 req.files。 ### 错误处理 ```js const multer = require("multer"); const upload = multer().single("avatar"); app.post("/profile", function (req, res) { upload(req, res, function (err) { if (err instanceof multer.MulterError) { // 发生错误 } else if (err) { // 发生错误 } // 一切都好 }); }); ``` ## orm 提供数据库操作 ```js // 初始化orm const orm = require("orm"); app.use( orm.express("mysql://test:123456@localhost:3306/test", { define: function (db, modeld, next) { next(); }, }) ); ``` ## node-uuid: 生成 UUID ```js const uuid = require("node-uuid"); uuid.v4(); // 生成随机的UUID ``` ## jsonwebtoken、express-jwt: 接口增加 token 验证 ### 配置中间件 ```js const { expressjwt: expressJWT } = require("express-jwt"); // secret: 加密秘钥 algorithms: 加密算法 router.use( expressJWT({ secret: "abcdefg", algorithms: ["HS256"] }).unless({ path: ["/user/login"], // 无需token验证的接口 }) ); // 配置中间件-错误捕获 router.use(function (err, req, res, next) { // token解析失败 const authorization = req.headers["authorization"]; if (!authorization) { return res.status(401).json({ code: config.codeObj.fail, msg: "用户未登录", }); } if (err.name === "UnauthorizedError") { return res.status(401).json({ code: config.codeObj.fail, msg: "token 失效,请重新登录", }); } }); ``` ### 生成 token ```js const jwt = require("jsonwebtoken"); router.post("/user/login", function (req, res) { const username = req.body.username; const password = req.body.password; if (username == "admin" && password == "123456") { let payload = { userName: "admin", }; const secretKey = "abcdefg"; const options = { expiresIn: "1h", // 过期时间1h }; const token = jwt.sign(payload, secretKey, options); return res.json({ code: "10000", token: token, }); } else { res.json({ code: "20000", message: "账号密码有误", }); } }); ``` ### 通过 token 获取用户信息 ```js // 获取用户信息 router.post("/user/getUserInfo", function (req, res) { res.json({ code: "10000", data: { userName: req.auth.userName, // admin }, }); }); ```