koa框架会用也会写—(koa-bodyparser、koa-better-body)

栏目: Node.js · 发布时间: 5年前

内容简介:上面的文字看起来可能不够直观,所以可以自己发送请求来查看:将login.html的form表单的enctype属性分别改成text/plain、application/x-www-form-urlencoded、multipart/form-data:从上面的两张图可以看到请求头中contentType中存在一个boundary属性,而请求体中的数据正是用这个boundary属性的值隔开的,去除里面的内容可以用分割的方法。由于文件是二进制的,需要分割buffer,但是Buffer的原型上没有这个方法,所以
  • koa-session:让无状态的http拥有状态,基于cookie实现的后台保存信息的session
  • koa-mysql:封装了需要用到的 SQL 语句
  • koa-mysql-session:当不想让session存储到内存,而想让session存储到 mysql 数据库中时使用
  • koa-router:后台会接受到各种请求的url,路由会根据不同的url来使用不同的处理逻辑。
  • koa-view:请求html页面时,后台会用模板引擎渲染数据到模板上,然后返回给后台
  • koa-static:请求img、js、css等文件时,不需要其他逻辑,只需要读取文件
  • koa-better-body:post上传文件时,解析请求体

koa系列文章:

form标签的enctype属性

  • application/x-www-form-urlencoded:在发送前编码所有字符(不设置默认)
  • multipart/form-data:不对字符编码,在使用包含文件上传控件的表单时,必须使用该值。
  • text/plain:空格转换为 "+" 加号,但不对特殊字符编码。

上面的文字看起来可能不够直观,所以可以自己发送请求来查看:

//前台页面login.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <link rel="stylesheet" href="/bootstrap/dist/css/bootstrap.css">
</head>
<body>
  <form action="/fileupload" enctype="text/plain" method="POST">
    <div class="form-group">
        <label for="username" class="control-label">用户名</label>
        <input type="text" class="form-control" id="username" name="username">
    </div>
    <div class="form-group">
        <label for="password" class="control-label">密码</label>
        <input type="text" class="form-control" id="password" name="password">
    </div>
    <div class="form-group">
        <label for="avatar" class="control-label">头像</label>
        <input type="file" multiple class="form-control" id="avatar" name="avatar">
    </div>
    <div class="form-group">
        <button type="submit" class="btn btn-danger">登录</button>
    </div>
  </form>
</body>
</html>
复制代码
//后台服务

const Koa = require('koa');
const path = require('path');
const Router = require('koa-router');
const static = require('koa-static');
const session = require('koa-session');

let app = new Koa();
let router = new Router();
app.keys = ['zfpx','jw'];
app.use(session({
  maxAge:5*1000,
}, app));

app.use(static(path.resolve(__dirname)));
app.use(static(path.resolve(__dirname,'node_modules')));

router.post('/login',async(ctx,next)=>{
  // 获取用户的账号及密码
  ctx.session.user = ctx.request.body
  ctx.body = ctx.session.user;
 
});
router.get('/home',async (ctx,next)=>{
    if (ctx.session.user){
        ctx.body = {
        status:1,
        username: ctx.session.user.username
        }
    }else{
        ctx.body = {
        status: 0,
        username: null
        }
    }
})
router.post('/fileupload',async(ctx,next)=>{
    console.log('upload')
})
app.use(router.routes());
app.listen(3000);
复制代码
koa框架会用也会写—(koa-bodyparser、koa-better-body)

将login.html的form表单的enctype属性分别改成text/plain、application/x-www-form-urlencoded、multipart/form-data:

  • text/plain的请求体:空格转换为 "+" 加号,但不对特殊字符编码
    koa框架会用也会写—(koa-bodyparser、koa-better-body)
  • application/x-www-form-urlencoded的请求体:在发送前编码所有字符(不设置默认)
    koa框架会用也会写—(koa-bodyparser、koa-better-body)
  • multipart/form-data:不对字符编码,在使用包含文件上传控件的表单时,必须使用该值。
    koa框架会用也会写—(koa-bodyparser、koa-better-body)
    从上面的图片可以看出来:
  1. 一般的表单提交使用的application/x-www-form-urlencoded,比text/plain多了字符串进行编码
  2. application/x-www-form-urlencoded、text/plain提交文件时会把只把文件名提交,内容在请求体中看不到。但是multipart/form-data却可以

koa-bodyparser和koa-better-body的区别

  • koa-bodyparser没有处理文件上传的功能,而koa-better-body处理了文件上传功能
  • koa-bodyparserh会将请求体挂载在ctx.request.body,而koa-better-body将请求体挂载在ctx.request.fields

koa-bodyparser的原理

//利用buffer来缓存数据,kao的中间件使用async和await
function bodyParser() {
  return async (ctx, next) => {
    await new Promise((resolve, reject) => {
      let arr = [];
      ctx.req.on('data', function (data) {
        arr.push(data);
      });
      ctx.req.on('end', function () {
        let r = Buffer.concat(arr).toString();
        ctx.request.body = r;
        resolve();
      })
    });
    await next();
  }
}
复制代码

koa-better-body的原理

koa框架会用也会写—(koa-bodyparser、koa-better-body)
koa框架会用也会写—(koa-bodyparser、koa-better-body)

从上面的两张图可以看到请求头中contentType中存在一个boundary属性,而请求体中的数据正是用这个boundary属性的值隔开的,去除里面的内容可以用分割的方法。由于文件是二进制的,需要分割buffer,但是Buffer的原型上没有这个方法,所以要扩展这个split方法:

Buffer.prototype.split = function (sep) {
  let len = Buffer.from(sep).length;    //分隔符的字节长度
  let pos = 0;
  let index = 0;
  let arr = [];
  //判断pos位后面是否还存在boundary
  //截取boundary前面的内容放在数组中
  while (-1 != (index = this.indexOf(sep,pos))) {
    arr.push(this.slice(pos,index));
    pos= len + index;//每个(**)b的位置
  }
  arr.push(this.slice(pos));//将最后boundary后面的内容放进数组
  return arr;
}
//b代表boundary,**代表获取的内容
let buffer = Buffer.form('b**b**b**b--').split('b')
console.log(buffer); 
//[<Buffer >,<Buffer 2a 2a>,<Buffer 2a 2a>,<Buffer 2a 2a>,<Buffer 2d 2d>]
复制代码

截取了需要的内容之后就可以做进一步的处理:

koa框架会用也会写—(koa-bodyparser、koa-better-body)
  • 内容中包含filename的获取除绿色部分的内容
  • 内容中不包含filename的组成ctx.request.fields.xx = xx
function betterBody({uploadDir}) {
    return async (ctx,next)=>{
      await new Promise((resolve,reject)=>{
        let arr = [];
        ctx.req.on('data', function (data) {
          arr.push(data);
        });
        ctx.req.on('end', function () {
          if (ctx.get('content-type').includes('multipart')) {
            let r = Buffer.concat(arr); //请求体中的内容
            
            //获取的boundary少了--,加上后用于截取内容
            let boundary = '--' + ctx.get('content-type').split('=')[1];  
            
            // 去除头尾取到中间有用的部分
            //[<Buffer 2a 2a>,<Buffer 2a 2a>,<Buffer 2a 2a>]
            let lines = r.split(boundary).slice(1, -1);
            
            let fileds = {};
            
            //处理含filename和不含filename的内容
            lines.forEach((line) => {
              let [head, content] = line.split('\r\n\r\n');
              head = head.toString();
              if (head.includes('filename')) {
                // 是文件取出除head+'\r\n\r\n'后的内容
                //-2表示去除最后空白行\r\n
                let content = line.slice(head.length + 4, -2);
                let uuid = require('uuid/v4');
                let fs = require('fs');
                let p = uuid();
                fs.writeFileSync(path.resolve(__dirname, uploadDir, p), content);
                fileds['path'] = p;
              } else {
                // 不是文件,挂载参数例如:username
                let key = head.match(/name="([\s\S]*)"/im)[1];
                //-2表示去除最后空白行\r\n
                let value = content.toString().slice(0, -2);
                fileds[key] = value;
              };
            })
            ctx.request.fields = fileds;
            resolve();
          }
        })
      })
      await next()
    }
}
复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

C++编程风格

C++编程风格

卡吉尔 / 聂雪军 / 机械工业出版社发行室 / 2007-1 / 25.00元

本书描述C++语言中较深层次的程序设计思想和使用方法,包含大量软件工程概念和设计模式,重点介绍大规模编程相关的内容,例如增加代码的可读性、可维护性、可扩展性以及执行效率等的方法。本书的示例代码都是从实际程序中抽取出来的,融人了作者的实际开发经验。讲解如何正确地编写代码以及避开一些常见的误区和陷阱,并给出了许多实用的编程规则,可快速提升读者的C++编程功力。   本书描述平实,示例丰富,适合有......一起来看看 《C++编程风格》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

在线进制转换器
在线进制转换器

各进制数互转换器