Node解析POST数据

原理

表单的POST提交主要有三种数据格式:

  1. text/plain
  2. application/x-www-form-urlencoded
  3. multipart/form-data

其中,text/plain用的很少;application/x-www-form-urlencoded是默认,采用url编码方式,以xxx=xxx&xxx=xx...格式传输数据;multipart/form-data用于上传文件内容。

当表单传输数据到服务端后,服务端会接受如下数据
Node解析POST数据_第1张图片

上面的数据格式可以简化为:

<分隔符>\r\n数据描述\r\n\r\n数据值\r\n
<分隔符>\r\n数据描述\r\n\r\n数据值\r\n
<分隔符>\r\n数据描述1\r\n数据描述2\r\n\r\n<文件内容>\r\n
<分隔符>–


实现思路

因此,服务端可以通过以下步骤来解析数据:
1、通过”<分隔符>“将数据进行切分

[ 空,
\r\n数据描述\r\n\r\n数据值\r\n,
\r\n数据描述\r\n\r\n数据值\r\n,
\r\n数据描述1\r\n数据描述2\r\n\r\n<文件内容>\r\n,
--\r\n]

2、丢弃头尾元素

[
\r\n数据描述\r\n\r\n数据值\r\n,
\r\n数据描述\r\n\r\n数据值\r\n,
\r\n数据描述1\r\n数据描述2\r\n\r\n<文件内容>\r\n
]

3、丢弃每一项头尾的\r\n

[
数据描述\r\n\r\n数据值,
数据描述\r\n\r\n数据值,
数据描述1\r\n数据描述2\r\n\r\n<文件内容>
]

4、用第一次出现的\r\n\r\n将数据进行切分

普通数据:[数据描述, 数据值]

文件数据:[数据描述1\r\n数据描述2, <文件内容>]

5、判断描述的里面有没有"\r\n"

有——文件数据:[数据描述1\r\n数据描述2, <文件内容>]
没有——普通数据:[数据描述, 数据值]


实例

获取分隔符

分隔符是一组随机字符串,如何获取分隔符?可以通过req.headers['content-type']获取
在这里插入图片描述

API

1.查找 indexOf()
2.截取 slice(s, e)
3.切分 split
由于Buffer不提供split方法,因此需要我们自己实现:

Buffer.prototype.split=Buffer.prototype.split||function (b){
  let arr=[];

  let cur=0;
  let n=0;
  while((n=this.indexOf(b, cur))!=-1){
    arr.push(this.slice(cur, n));
    cur=n+b.length;
  }

  arr.push(this.slice(cur));

  return arr;
};

完整代码

const http=require('http');
const common=require('./libs/common');    //split 方法
const fs=require('fs');
const uuid=require('uuid/v4');

let server=http.createServer((req, res)=>{
  let arr = [];
  req.on('data', data => {
    arr.push(data);
  });

  req.on('end', () => {
    let data = Buffer.concat(arr);

    let post = {}; //普通数据
    let files = {};  // 文本数据

    if (req.headers['content-type']) {
      let str = req.headers['content-type'].split(';')[1];

      if(str) {
        let boundary = '--' + str.split('=')[1];

        // 1、通过<分隔符>将数据进行切分
        let arr = data.split(boundary);

        // 2、丢弃头尾元素
        arr.shift();
        arr.pop();

        // 3、丢弃每一项头尾的\r\n
        arr = arr.map((elem) => elem.slice(2, elem.length-2));

        // 4、用第一次出现的\r\n\r\n将数据进行切分
        arr.forEach(elem => {
          let n = elem.indexOf('\r\n\r\n');

          let disposition = elem.slice(0, n);
          let content = elem.slice(n+4);

          disposition = disposition.toString();

          //5、判断描述的里面有没有"\r\n"
          if(disposition.indexOf('\r\n') == -1) {
            // console.log('普通数据');

            //Content-Disposition: form-data; name="user"
            content = content.toString();

            let name = disposition.split(';')[1].split('=')[1];
            name = name.substring(1, name.length-1);

            post[name] = content;
          }else {
            // console.log('文件数据');

            /*Content-Disposition: form-data; name="f1"; filename="a.txt"\r\n
            Content-Type: text/plain*/
            let [,name,filename] = disposition.split('\r\n')[0].split(';');
            let type = disposition.split('\r\n')[1].split(':')[1];

            name = name.split('=')[1].substring(1,name.length-1);
            filename = filename.split('=')[1].substring(1,filename.length-1);

            let path = `upload/${uuid().replace(/\-/g, '')}`;

            fs.writeFile(path, content, err=>{
              if(err){
                console.log('文件写入失败', err);
              }else{
                files[name]={filename, path, type};
                console.log(files);
              }
            });
          }

        })
        console.log(post);
      }
    }
    res.end();
  })
});
server.listen(1337);

输出:
在这里插入图片描述

你可能感兴趣的:(node)