首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >NodeJs原生文件上传理解

NodeJs原生文件上传理解

作者头像
切图仔
发布于 2022-09-08 11:18:06
发布于 2022-09-08 11:18:06
1.6K00
代码可运行
举报
文章被收录于专栏:生如夏花绚烂生如夏花绚烂
运行总次数:0
代码可运行

使用nodejs实现文件上传比较麻烦,本文只是方便理解原理简单实现

1.准备html页面

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!DOCTYPE html>
<html lang="zh-CN">
<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>
</head>
<body>
    <form action="http://localhost:8080" enctype="multipart/form-data">
        姓名:<input type="text" name="username">
        <br />
        密码:<input type="password" name="password">
        <br />
        文件:<input type="file" name='file'>
        <br />
        <input type="submit" value="提交" name="">
    </form>
    
</body>
</html>

2.编写nodejs代码接收前台数据

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const http = require('http');
http.createServer((req,res)=>{
    let arr = [];
    req.on('data',buffer=>{
        arr.push(buffer);
    })
    req.on('end',()=>{
        console.log(arr.toString());
    })
}).listen(8080);

这里我们为了方便查看提交的数据,我们将buffer数据转换为普通字符串 当提交时结果如下‘

可以看到数据被分隔符(——-WebKit……..)分隔成了好几段 第一段:用户名 第二段:密码 第三段:文件内容 文件内容多了一行文件类型说明

数据获取成功,但我们怎么将这些数据格式化 如(username:dfdf,password:dedse....)的形式在不借助第三方模块的情况下。 通过观察我们发现,他的数据结构大概是这样

<分隔符>\r\n字段信息\r\n\r\n内容\r\n<分隔符>\r\n字段信息\r\n\r\n内容\r\n<分隔符>\r\n\字段信息\r\n文件类型\r\n\r\n内容\r\n<分隔符>--’ \r\n表示换行。

要将这些数据格式化,首先我们先从<分隔符>切割,当分隔符被切割后,上面数据应该变为 1.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[
    null,
    \r\n字段信息\r\n\r\n内容\r\n,
    \r\n字段信息\r\n\r\n内容\r\n,
    \r\n\字段信息\r\n文件类型\r\n\r\n内容\r\n,
    --
]

2.此时在将第0个和最后一个去掉得到

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[
 \r\n字段信息\r\n\r\n内容\r\n,
 \r\n字段信息\r\n\r\n内容\r\n,
 \r\n\字段信息\r\n文件类型\r\n\r\n内容\r\n,
]

3.在将数组中的每个元素的第一个换行和最后一个换行去掉得到如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[
 字段信息\r\n\r\n内容,
 字段信息\r\n\r\n内容,
 字段信息\r\n文件类型\r\n\r\n内容,
]

4.对于普通数据我们在通过字符串切割,切掉\r\n\r\n即可

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
字段信息,内容
字段信息,内容
...

思路大致明了,但前台提交的是buffer数据,我们不能直接将其转换为字符串在通过字符串的方法进行切割。

不过好在Buffer为我们提供了一些方法,这些方法与字符串的方法一样。 buffer.indexOf(str):查找str在buffer数据中的位置 buffer.slice(start,end) 类似于js中的substring

注意:buffer中没有切分数据的方法

通过以上两个方法我们就能实现buffer数据的切分 如下示例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let buffer = new Buffer('abc\r\nbsdk\r\nrgg');//定义一个buffer数据,使用分隔符隔开
function bufferSplit(buffer,deli){
    let arr = [];//存储最终数据
    let n = 0;//分隔符开始的位置
    //如果分隔符存在
    while((n=buffer.indexOf(deli))!=-1){
        arr.push(buffer.slice(0,n));//abc
        buffer=buffer.slice(n+deli.length);
        //第一次buffer=bsdk\r\n\rgg
        //第二次buffer=\r\nrgg
        ..
    }
    arr.push(buffer);
    return arr;
}

console.log(bufferSpli(buffer,'\r\n').toString());
//返回 abc,bsdk,rgg

我们通过上面的函数加简单的分隔了buffer数据,现在我们将他应用到文件上传项目上

将这个函数定义成一个模块

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
exports.bufferSplit=function(buffer,deli){
    let arr = [];
    let n = 0;
    while((n=buffer.indexOf(deli))!=-1){

        arr.push(buffer.slice(0,n));
        buffer=buffer.slice(n+deli.length);
    }
    arr.push(buffer);
    return arr;
}

使用这个模块切割分隔符 上面我们所说的分隔符其实在 req.headers

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const http = require('http');
const util = require('uti');
http.createServer((req,res)=>{
    console.log(req.headers);
    let arr = [];
    req.on('data',buffer=>{
        arr.push(buffer);
    })
    req.on('end',()=>{
        let buffer = Buffer.concat(arr);
        
    })
}).listen(8080);

需要注意的是headers里面的分隔符比我们最后得到的分隔符少两个‘-’,我们在获取分隔符的时候要注意加两个-

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//最后得到:
------WebKitFormBoundary0qjsU7Tl6U1BApvt

//headers:
----WebKitFormBoundary0qjsU7Tl6U1BApvt

获取分隔符片段

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let boundary='--'+req.headers['content-type'].split('; ')[1].split('=')[1];
console.log(boundary);
//------WebKitFormBoundaryn3lpgLIrRcqpTTWi

切割分隔符

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const http = require('http');
const util = require('uti');
http.createServer((req,res)=>{
    // console.log(req.headers);
    let boundary='--'+req.headers['content-type'].split('; ')[1].split('=')[1];
    // console.log(boundary);

    let arr = [];
    req.on('data',buffer=>{
        arr.push(buffer);
    })
    req.on('end',()=>{

        let buffer = Buffer.concat(arr);
        //切割分隔符
        let result = util.bufferSplit(buffer,boundary);
        console.log(result.toString());
    })
}).listen(8080);

数据和我们料想的一样

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[
    null,
    \r\n字段信息\r\n\r\n内容\r\n,
    \r\n字段信息\r\n\r\n内容\r\n,
    \r\n\字段信息\r\n文件类型\r\n\r\n内容\r\n,
    --
]

删除第一行和最后一个元素‘

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
req.on('end',()=>{
        let buffer = Buffer.concat(arr);
        //切割分隔符
        let result = util.bufferSplit(buffer,boundary);
        //删除第一个和最后一个元素
        result.pop();
        result.shift();
        console.log(result.toString());


    })

对数组中的每一个元素处理

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
req.on('end',()=>{
        let buffer = Buffer.concat(arr);
        //切割分隔符
        let result = util.bufferSplit(buffer,boundary);
        result.pop();
        result.shift();
        // console.log(result.toString());
        //对每一个元素进行处理
        result.forEach(buffer=>{
            buffer = buffer.slice(2,buffer.length-2);//切掉buffer前面和后面的换行
            let n = buffer.indexOf('\r\n\r\n');//获取分分隔符的位置
            let info = buffer.slice(0, n).toString();//获取字段信息
            let data = buffer.slice(n+4);//获取字段内容

            console.log(info);
            console.log(data.toString());
        })


    })

打印结构如下

判断文件数据和普通数据获取字段名/文件名

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
req.on('end',()=>{
        let buffer = Buffer.concat(arr);
        //1.切割分隔符
        let result = util.bufferSplit(buffer,boundary);
        result.pop();
        result.shift();
        // console.log(result.toString());
        //2.对每一个元素进行处理
        result.forEach(buffer=>{
            //3.切断buffer前面和后面的换行
            buffer = buffer.slice(2,buffer.length-2);
            //4.去掉每个元素的\r\n\r\n
            let n = buffer.indexOf('\r\n\r\n');//获取分分隔符的位置
            let info = buffer.slice(0, n).toString();//获取字段信息

            let data = buffer.slice(n+4);//获取字段内容
            // console.log(info);
            // console.log(data.toString());
            //5.判断文件和普通数据
            if(info.indexOf('\r\n')!=-1){
                //5.1文件信息 获取字段名和文件名
                let res2 = info.split('\r\n')[0].split('; ');
                let name = res2[1].split('=')[1];//获取字段名
                let filename = res2[2].split('=')[1];//获取文件名
                name = name.substring(1, name.length-1);//去掉引号
                filename = filename.substring(1, filename.length-1);
                console.log(name);
                console.log(filename);
            }else{
                //5.2普通信息 获取字段名
                let name = info.split('; ')[1].split('=')[1];
                name = name.substring(1, name.length-1);
                console.log(name+':'+data);
                //username:admin
                //password:23456754
            }
        })

    })

通过fs模块实现文件上传

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
req.on('end',()=>{

        let buffer = Buffer.concat(arr);
        //1.切割分隔符
        let result = util.bufferSplit(buffer,boundary);
        result.pop();
        result.shift();
        // console.log(result.toString());
        //2.对每一个元素进行处理
        result.forEach(buffer=>{
            //3.切断buffer前面和后面的换行
            buffer = buffer.slice(2,buffer.length-2);
            //4.去掉每个元素的\r\n\r\n
            let n = buffer.indexOf('\r\n\r\n');//获取分分隔符的位置
            let info = buffer.slice(0, n).toString();//获取字段信息

            let data = buffer.slice(n+4);//获取字段内容
            // console.log(info);
            // console.log(data.toString());
            //5.判断文件和普通数据
            if(info.indexOf('\r\n')!=-1){
                //5.1文件信息 获取字段名和文件名
                let res2 = info.split('\r\n')[0].split('; ');
                let name = res2[1].split('=')[1];//获取字段名
                let filename = res2[2].split('=')[1];//获取文件名
                name = name.substring(1, name.length-1);//去掉引号
                filename = filename.substring(1, filename.length-1);
                // console.log(name);
                // console.log(filename);
                //文件上传
                fs.writeFile(`upload/${filename}`,data,err=>{
                    if(err){
                        console.log(err);
                    }else{
                        console.log('上传成功');
                    }
                });

            }else{
                //5.2普通信息 获取字段名
                let name = info.split('; ')[1].split('=')[1];
                name = name.substring(1, name.length-1);
                console.log(name+':'+data);
            }
        })

    })

成功上传文件

到这里使用nodejs原生简单实现文件上传已经完成,但实在是太麻烦了,且还有很多没完善的地方,本文只为理解原理。

在我们真正做文件上传时可以借助第三方模块来完成 如multiparty模块 官方网址:https://www.npmjs.com/package/multiparty

通过multiparty实现文件上传

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const http = require('http');
const multiparty = require('multiparty');
http.createServer((req,res)=>{
    let form = new multiparty.Form({
        uploadDir:'./upload'//指定上传的文件路径
    });
    form.parse(req);
    //接收普通数据
    form.on('field',(name,value)=>{
        // name:字段名
        // value:值
        console.log('数据:',name,value);

    })
    //接收文件数据
    form.on('file',(name,file)=>{
        console.log('文件:',name,file);
    })
    //表单解析完成
    form.on('close',()=>{
        console.log('完成');
    })
}).listen(8080);
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-05-15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档