详解KOA2如何手写中间件(装饰器模式)

2020-06-17 06:53:22易采站长站整理

const path = require("path");
const fs = require("fs");
const uuid = require("uuid/v1"); // 生成随机串

const app = new Koa();

// 将 koa-better-body 中间件从 koa 1.0 转化成 koa 2.0,并使用中间件
app.use(convert(betterBody({
uploadDir: path.resolve(__dirname, "upload")
})));

app.use(async (ctx, next) => {
if (ctx.path === "/" && ctx.method === "POST") {
// 使用中间件后 ctx.request.fields 属性自动加上了 post 请求的文件数据
console.log(ctx.request.fields);

// 将文件重命名
let imgPath = ctx.request.fields.avatar[0].path;
let newPath = path.resolve(__dirname, uuid());
fs.rename(imgPath, newPath);
}
});

app.listen(3000);

上面代码中 koa-better-body 的主要功能就是将表单上传的文件存入本地指定的文件夹下,并将文件流对象挂在了 ctx.request.fields 属性上,我们接下来就模拟 koa-better-body 的功能实现一版基于 Koa 2.x 处理文件上传的中间件。

文件:my-koa-better-body.js


const fs = require("fs");
const uuid = require("uuid/v1");
const path = require("path");

// 给 Buffer 扩展 split 方法预备后面使用
Buffer.prototype.split = function (sep) {
let len = Buffer.from(sep).length; // 分隔符所占的字节数
let result = []; // 返回的数组
let start = 0; // 查找 Buffer 的起始位置
let offset = 0; // 偏移量

// 循环查找分隔符
while ((offset = this.indexOf(sep, start)) !== -1) {
// 将分隔符之前的部分截取出来存入
result.push(this.slice(start, offset));
start = offset + len;
}

// 处理剩下的部分
result.push(this.slice(start));

// 返回结果
return result;
}

module.exports = function (options) {
return async (ctx, next) => {
await new Promise((resolve, reject) => {
let dataArr = []; // 存储读取的数据

// 读取数据
ctx.req.on("data", data => dataArr.push(data));

ctx.req.on("end", () => {
// 取到请求体每段的分割线字符串
let bondery = `--${ctx.get("content-Type").split("=")[1]}`;

// 获取不同系统的换行符
let lineBreak = process.platform === "win32" ? "rn" : "n";

// 非文件类型数据的最终返回结果
let fields = {};

// 分隔的 buffer 去掉没用的头和尾即开头的 '' 和末尾的 '--'
dataArr = dataArr.split(bondery).slice(1, -1);

// 循环处理 dataArr 中每一段 Buffer 的内容
dataArr.forEach(lines => {
// 对于普通值,信息由包含键名的行 + 两个换行 + 数据值 + 换行组成
// 对于文件,信息由包含 filename 的行 + 两个换行 + 文件内容 + 换行组成