Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
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 删除。

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
各种字符编码详解
serena
2017/09/19
2.2K0
各种字符编码详解
精述字符编码
带你了解ASCII,Latin1,ANSI,Unicode,UCS-2,UCS-4,UTF-8,UTF-16,UTF-32,GB2312,GB13000,GBK,GB18030,BIG5,BMP,Code Page,BOM,MBCS,Little Endian,Big Endian,内码,外码。
恋喵大鲤鱼
2018/08/03
1.6K0
精述字符编码
字符编码技术专题(五):前端必读的计算机字符编码知识入门
以上这些问题都涉及到计算机中*础的知识点——字符集及字符编码的概念,本篇将从前端开发人员的视解,让你彻底搞清并理解这些问题的本质。
JackJiang
2023/09/28
5100
字符编码技术专题(五):前端必读的计算机字符编码知识入门
刨根究底字符编码之八——Unicode编码方案概述
前面讲过,随着计算机发展到世界各地,于是各个国家和地区各自为政,搞出了很多既兼容ASCII但又互相不兼容的各种编码方案。这样一来同一个二进制编码就有可能被解释成不同的字符,导致不同的字符集在交换数据时带来极大的不便。
笨笨阿林
2022/05/09
9830
刨根究底字符编码之八——Unicode编码方案概述
刨根究底字符编码之五——简体汉字编码方案(GB2312、GBK、GB18030、GB13000)以及全角、半角、CJK
英文字母再加一些其他标点字符之类的也不会超过256个,用一个字节来表示一个字符就足够了(2^8 = 256)。但其他一些文字不止这么多字符,比如中文中的汉字就多达10多万个,一个字节只能表示256个字符,肯定是不够的,因此只能使用多个字节来表示一个字符。
笨笨阿林
2022/05/09
4.9K0
刨根究底字符编码之五——简体汉字编码方案(GB2312、GBK、GB18030、GB13000)以及全角、半角、CJK
字符,字符集,字符编码
现在Unicode已然一统天下,我想很多年轻的程序员可能都没遇到过编码问题,更不用说了解编码的发展了。前些日子在一个老网站上偶遇乱码,虽然入行时间不短,但对其究竟也是不甚了解,好奇心驱使下落入深坑。还好经过一段时间的摸爬滚打,边学边写,总算大概理清了个脉络,记录之,分享之。
_春华秋实
2019/02/22
1.9K0
字符,字符集,字符编码
python encode和decode函数说明[通俗易懂]
如上面代码,str\str1\str2均为字符串类型(str),给字符串操作带来较大的复杂性。
全栈程序员站长
2022/09/07
1.6K0
常见字符集&乱码问题
位数:ASCII是用7位表示的,能表示128个字符;其扩展使用8位表示,表示256个字符。
全栈程序员站长
2022/09/05
7900
Unicode编码
这是一篇程序员写给程序员的趣味读物。所谓趣味是指可以比较轻松地了解一些原来不清楚的概念,增进知识,类似于打RPG游戏的升级。整理这篇文章的动机是两个问题:
硬核编程
2019/08/19
1.6K0
理清字符集和字符编码关系
计算机内部由集成电路(Integrated Circuit,IC)构成,IC的所有引脚,只有直流电压0V和5V两个状态。也就是说,IC的一个引脚,只能表示两个状态。正是由于这个原因,决定了计算机的信息只能用二进制数处理。
木可大大
2018/04/10
1.8K7
理清字符集和字符编码关系
刨根究底字符编码之十三——UTF-16编码方式
UTF-16编码方式源于UCS-2(Universal Character Set coded in 2 octets、2-byte Universal Character Set)。而UCS-2,是早期遗留下来的历史产物。
笨笨阿林
2019/01/18
1.2K0
关于编码,看这个系列就够了!!!
做通信的时候,或者使用IO流读写数据的时候脑袋总是晕晕乎乎的。 与计算机打交道久了,不可避免会看到“鬼画符”式的乱码,让人云里雾里的,特别烦人。
烟雨平生
2025/03/27
1720
关于编码,看这个系列就够了!!!
字符集
本文主要讲解字符集和字符编码的一些概念,通常我们所说的字符集其实指的包含了字符编码集+字符编码。但字符集有时候有时候又只是字符编码集的简称,具体语义根据上下文判断理解就行,也不是必须分的很清楚。
@阿诚
2020/09/01
2K0
字符集
计算机字符编码的前世今生
有人丢给你下面这张图,如果你能清楚地说明它们之间的关系以及用途,那么你对字符编码的理解肯定过关了。
2020labs小助手
2021/08/16
4320
字符编码
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://polaris.blog.51cto.com/1146394/377468
拾点阳光
2018/09/28
2.4K0
从JavaScript看字符编码的前世今生!
导语 | 每个程序员都应该了解一下字符编码,有了基础概念之后我们对编程语言、字符处理能有更深入的理解。本文我花了大量时间进行资料查阅和考证,希望能够给大家带来一些帮助,多多交流! 一、起因 最近在研究Babel的源码,在看到Acorn词法解析源码中有这样一段逻辑: pp.fullCharCodeAtPos = function() { let code = this.input.charCodeAt(this.pos) if (code <= 0xd7ff || code >= 0xdc00
腾讯云开发者
2022/05/18
9310
从JavaScript看字符编码的前世今生!
字符集与字符编码总结
转载请注明出处。请前往 Tiga on Tech 查看原文以及更多有趣的技术文章。
Tiga
2020/01/23
1.3K0
字符集与字符编码总结
万字长文总结JAVA几种常见的编码格式和乱码原因分析
编码问题一直困扰着开发人员,尤其在 Java 中更加明显,因为 Java 是跨平台语言,不同平台之间编码之间的切换较多。本文将向你详细介绍 Java 中编码问题出现的根本原因,你将了解到:Java 中经常遇到的几种编码格式的区别;Java 中经常需要编码的场景;出现中文问题的原因分析;在开发 Java web 程序时可能会存在编码的几个地方,一个 HTTP 请求怎么控制编码格式?如何避免出现中文问题?
业余草
2021/02/04
1.1K0
万字长文总结JAVA几种常见的编码格式和乱码原因分析
编码知识大杂烩
ASCII码 (American Standard Code for Information Interchange,美国标准信息交换代码),最原始最直观的表示方式,一个字节表示一个字符,一个字节=8位,那么一个字节就有256(2的8次方)种状态。这又分为标准ASCII和扩展ASCII,其中:
黑泽君
2018/10/12
5300
关于字符编码
在计算机中,所有的数据在存储和运算时都是使用二进制数表示。为了互相通信,就必须使用相同的编码规则。ANSI (American National Standard Institute)在1967年发表了一套单字节字符编码方案 - ASCII (American Standard Code for Information Interchange)。后来ISO (International Organization for Standardization) 和IEC (International Electrotechnical Commission)将其定为国际标准 - ISO/IEC 646
Taishan3721
2021/12/02
9740
关于字符编码
相关推荐
各种字符编码详解
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验