美文网首页
POST请求,文件上传/解析数据/UUID/流操作、gz压缩

POST请求,文件上传/解析数据/UUID/流操作、gz压缩

作者: 子心_ | 来源:发表于2019-07-09 09:37 被阅读0次

    POST请求,文件上传

    server.js

        const http = require('http');
        let server = http.createServer((req,res)=>{
            let arr=[];//使用数组接收data
            req.on('data',(data)=>{
                arr.push(data);
            })
            req.on('end',()=>{
                //post传输过来的数据被分了很多块,需要自己进行解析
                //将数组放到Buffer中、Buffer已经被放在node中,所以不需要引入模块
                //转成Buffer是因为需要将数据保存在二进制状态下进行操作,否则会造成图片等数据损坏.
                let data = Buffer.concat(arr);
                console.log(data);
                res.end();
            })
        })
    server.listen(8087)
    

    HTML

        <form action="http://localhost:8087/aa" method="post"   enctype="multipart/form-data">//enctype值看扩展【表单的三种POST】
            <input type="text" name="user">
            <input type="password" name="pass">
            <input type="file" name="file">
            <input type="submit" value="提交">
        </form>
    

    解析数据

        //server.js中,将数据放到Buffer中之后,需要对Buffer进行解析,
        //解析掉分隔符,\r\n,拆分数据描述与数据值.
        //对Buffer进行操作所需函数(例:分隔符为`):
        let b = new Buffer("`aaa`bbbb`bbcc");
        b.indexOf("`")//返回第一个`所在的下标=>0,有第二个参数,index是指从第几个位置开始找.
        b.slice(0,5);//包含开头,不包含结尾=>`aaa`
        b.split("`");//暂时不支持该函数,这里自己动手写呗;
        //如果Buffer中有split函数,就用split,如果没有就用自己的函数,参数b是分隔符.
        Buffer.prototype.split=Buffer.prototype.split||function(b){
            let arr = [];//接受数据用的数组
            let cur = 0;//代表当前已经切分到哪个位置
            let n = 0;//切分时用的结束下标
            while((n=this.indexOf(b,cur))!=-1){//结束下标等于当前对象的第一个分隔符出现的位置,并且不等于-1.
                arr.push(this.slice(cur,n));//将当前对象切分,从上次切分到的位置到当前找到的分隔符的位置.
                cur = n+b.length;//更新当前的切分位置为,当前找到的分隔符位置加分隔符长度.
            };
            arr.push(this.slice(bur));//将最后一段添加到数组中;
            return arr;
        }
        let arr = b.split("`");//[aaa,bbbb,bbcc];
        //实际应用中POST传输过来的数据分隔符是随即的,被记录在req.hearders对象的content-type中,
        //可以使用req.hearders[content-type]自己解析获得.
    

    实际的server.js解析

        const http = require('http');
        const uuid = require('uuid/v4');
        const fs = require('fs');
        let server = http.createServer((req,res)=>{
            let arr=[];//使用数组接收data
            req.on('data',(data)=>{
                arr.push(data);
            })
            req.on('end',()=>{
                Buffer.prototype.split = Buffer.prototype.split||function(b){//自己动手写split函数
                    let arr = [];//接受数据用的数组
                    let cur = 0;//代表当前已经切分到哪个位置
                    let n = 0;//切分时用的结束下标
                    while((n=this.indexOf(b,cur))!=-1){//结束下标等于当前对象的第一个分隔符出现的位置,并且不等于-1.
                        arr.push(this.slice(cur,n));//将当前对象切分,从上次切分到的位置到当前找到的分隔符的位置.
                        cur = n+b.length;//更新当前的切分位置为,当前找到的分隔符位置加分隔符长度.
                    };
                    arr.push(this.slice(cur));//将最后一段添加到数组中;
                    return arr;
                }
                let post={};//用来放post请求的结果
                let file={};//用来放文件
                let data = Buffer.concat(arr);//将接收到的数组放到Buffer中
                if(req.headers['content-type']){//判断是否有req.headers['content-type']
                    let head = '--'+req.headers['content-type'].split("; ")[1].split("=")[1];//切割得出分隔符
                    let arrs = data.split(head);//将二进制的Buffer通过分隔符进行初次分割
                    arrs.shift();//去掉数组第一个==》是个null
                    arrs.pop();//去掉数组最后一个==》是个--\r\n
                    arrs = arrs.map(buf=>buf.slice(2,buf.length-2));//使用map遍历,去掉数组中每个元素的开头两位与结束两位\r\n
                    arrs.forEach(element => {//对数组进行循环
                        let n =element.indexOf('\r\n\r\n');//找出数组元素中\r\n\r\n所在的位置
                        let disposition = element.slice(0,n);//得到数据描述
                        let content = element.slice(n+4);//得到数据值
                        disposition= disposition.toString();
                        if(disposition.indexOf('\r\n')==-1){//等于-1就是普通数据
                            content = content.toString();//将值转成字符串
                            let name = disposition.split('; ')[1].split('=')[1];//切割出参数中的name
                            name = name.substring(1,name.length-1);//切割出参数中的name
                            post[name] = content;//赋值给post
                        }else{//对文件进行操作
                            let [line1,line2] = disposition.split('\r\n');
                            let [,name,filename] = line1.split('; ');
                            let type = line2.split(': ')[1];
                            name = name.split('=')[1];
                            name = name.substring(1,name.length-1)
                            filename = filename.split('=')[1];
                            filename = filename.substring(1,filename.length-1)
                            console.log(name,filename,type);
                            console.log(content); //文件就是二进制,不需要转换成字符串,否则会造成写入失败
                            let path = `file/${uuid().replace(/\-/g,"")}`;
                            fs.writeFile(path,content,(err)=>{
                                if(err){
                                    console.log('失败');
                                    
                                }else{
                                    file[name] = {filename,path,type};
                                    console.log(file);
                                }
                            });
                        }
                    });
                    console.log(post);
                }
                res.end();
            })
        })
    
        server.listen(8087)
    

    UUID(nodejs的第三方模块,需要使用npm下载)

        cnpm i uuid -D;安装在当前文件夹下;
        const uuid = require('uuid/v4');
        console.log(uuid());
    

    流操作、gz压缩

        // 1. 上面的代码会等到全部数据都上传结束以后才开始处理.
        // 2. readFile 会先把所有数据都读到内存在,然后再处理函数,这样非常占用内存,而且资源利用不充分.
        // 解决如上问题,就需要使用流.
        // 流操作是每次收到一部分就解析一部分.不再是全部读取之后才进行操作.
        // 1. 读取流 fs.createRaedStream()、req
        // 2. 写入流 fs.createWriteStream()、res
        // 3. 读写流 压缩 const zlib = require('zlib'); zlib模块
       // 例(文件流):
        const fs = require('fs');
        let ins = fs.createReadStream('1.png');//创建读取流
        let outs = fs.createWriteStream('2.png');//创建写入流
        ins.pipe(outs);//创建管道,连接两个流
        ins.on('error',err=>{//读取流监听
            console.log('读取失败处理方法')
        })
        ins.on('end',()=>{//读取流监听
            console.log('读取完成方法')
        })
        outs.on('finish',()=>{//写入流监听
            console.log('写入完成方法')
        })
        //例(网络流):
        const http = require('http');
        const fs = require('fs');
        let server = http.createServer((req,res)=>{
            let ins = fs.createReadStream(`www${req.url}`);//得到用户请求的地址
            ins.pipe(res);//如果有就写给用户
            ins.on('error',err=>{
                res.writeHeader(404);
                res.write('Not Found');
                res.end();
            })
        });
        server.listen(8087);
        //例(读写流,gz压缩)
        const zlib = require('zlib');
        const fs = require('fs');
        let ins = fs.createReadStream('1.png');//创建读取流
        let outs = fs.createWriteStream('2.png.gz');//创建写入流(后缀gz)
        let gz = zlib.createGzip();//创建gz管道
        ins.pipe(gz).pipe(outs);//将读取流连接gz在连接写入流
        outs.on('finish',()=>{
            console.log('成功');
        })
        //最终例:在网络流中将文件以压缩的形式发送出去,节省流量,省钱.
        const http = require('http');
        const fs = require('fs');
        const zlib = require('zlib');
        let server = http.createServer((req,res)=>{
            let ins = fs.createReadStream(`www${req.url}`);//得到用户请求的地址
            //不在是直接将文件给用户,而是经由gz压缩后再给.
            res.setHeader('content-encoding','gzip');//需要告诉浏览器,传输给浏览器的是一个压缩版,否则浏览器会当做下载压缩文件
            let gz = zlib.createGzip();//创建gz管道
            ins.pipe(gz).pipe(res);//将读取的文件压缩后发送给浏览器//节约流量
    
    
            ins.on('error',err=>{
                res.setHeader('content-encoding','');//处理错误时返回的不是NOT found是个文本而不是页面,所以要把头重置,否则就会报错.
                res.writeHeader(404);
                res.write('Not Found');
                res.end();
            })
        });
        server.listen(8087);
    

    扩展

    表单的三种POST提交的enctype属性
    <form action='xxx'  method='post' enctype='text/plain'>
        1. text/plain  指该post请求是纯文本.
        2. application/x-www/form-urlencoded  默认的,指该post请求为url编码方式.
        3. multipart/form-data  指该post是上传一个文件. 

    相关文章

      网友评论

          本文标题:POST请求,文件上传/解析数据/UUID/流操作、gz压缩

          本文链接:https://www.haomeiwen.com/subject/boetkctx.html