在 Nodejs-基础-http 篇介绍了node如何搭一个简单的服务器javascript
在 Nodejs-基础-fs 篇介绍了node若是根据请求返回静态文件 本篇就来继续介绍一下NodeJS如何处理请求html
相信看本篇文章的你们仍是前端人员比较多,这里稍微说一下,前端数据请求有几种,好比form、ajax、jsonp 这些在页面里写的过程确定是千差万别,可是在后台处理起来来讲,都是同样的,无论多少种。 由于先后端的请求无论怎么样,都是走的HTTP请求。前端
对后台来讲主要是请求方式的区别 好比 GET 和 POST java
首先先建一个 server.js
node
server.jsajax
const http = require('http');
http
.createServer((req, res) => {
res.write('success');
res.end();
})
.listen(8080);
复制代码
而后建一个form.html form.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>
</head>
<body>
<form action="http://localhost:8080/" method="GET">
用户:<input type="text" name="user" value="" /><br/>
密码:<input type="password" name="pass" value="" /><br/>
<input type="submit" value="提交">
</form>
</body>
</html>
复制代码
这时候运行 node server.js
express
而后打开form.html,表单随便输入点东西,而后点击提交,不出意外的话会看到这样 json
这样基本请求数据就已经搞好了,可是这不是重点,重点是得后台能拿到这些数据对不对 其实get数据特别简单,他的数据是在地址里的,既然在地址里是否是就能够找 req.url
里拿呀,若是对这里有问题欢迎观看 Nodejs-基础-http 这篇文章,里面有详细说明后端
废话很少说,直接打印出来看看 这里先把form的地址随便修改点东西,为了看着方便,像这样
而后修改一下 server.js
const http = require('http');
http
.createServer((req, res) => {
console.log(req.url);
res.write('success');
res.end();
})
.listen(8080);
复制代码
而后从新运行,提交
这个 /favicon.ico
以前 Nodejs-基础-http 篇也说过,是浏览器主动管你要的,直接判断一下return就好 可是拿到 /aaa/?user=name&pass=123456
这一堆东西毕竟不能直接用,因此我们来切一下 (这里确定是有不少现成的方法的,包括各类基于node的框架,都会把这些参数给封装好,可是这里主要说明原理,毕竟他们也是这么作的)
const http = require('http');
http
.createServer((req, res) => {
if (req.url.indexOf('?') == -1) return; // 若是请求地址不带get数据的化 直接返回
let arr = req.url.split('?');
//arr[0] => 地址 /aaa
//arr[1] => 数据 user=name&pass=123456
let url = arr[0];
let arr2 = arr[1].split('&');
//arr2 => ['user=name','pass=123456']
let GET = {};
for (let i = 0; i < arr2.length; i++) {
let arr3 = arr2[i].split('=');
// arr3[0] => key
// arr3[1] => value
GET[arr3[0]] = arr3[1];
}
console.log(url, GET);
res.write('success');
res.end();
})
.listen(8080);
复制代码
这段代码大概意思相信你们都能看明白,就是把字符串切成最终的数据,能够保存而后从新启动提交看一下终端
地址和请求的数据就都拿到了,是否是很简单?
固然了,真正项目里确定不能每次都写着一大坨,相信看到这已经有人想到了,封装成一个函数,对不对? 哈哈,其实也不用,NodeJS官方有这方面的包,叫 queryString
,所谓的查询字符串 拿刚才的例子来稍微修改一下
const http = require('http');
const querystring = require('querystring');
http
.createServer((req, res) => {
let GET = {};
if (req.url.indexOf('?') != -1) {
var arr = req.url.split('?');
var url = arr[0];
GET = querystring.parse(arr[1]);
} else {
var url = req.url;
}
console.log(url, GET);
res.write('success');
res.end();
})
.listen(8080);
复制代码
是否是方便了许多呢?
固然确定有人以为,仍是不方便,仍是得 split 一刀,怎么说呢,懒确实能推动人类社会前进,是的NodeJS还有另外一个包,能够直接帮你把URL地址里的信息全都解析好,真棒对吧~ 咱们能够先来试验一下 这里直接用百度随便搜索一条的地址了
代码以下
const url = require('url');
var obj = url.parse(
'https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=111&rsv_pq=859f07e2001f0043&rsv_t=57ad4pr9cVROAk8L%2BYHrRif%2BoJFfODwoMcmk%2Bm4il9T82sQeP%2FbI4nY9Vlc&rqlang=cn&rsv_enter=1&rsv_dl=tb&rsv_sug3=3&rsv_sug1=3&rsv_sug7=100&rsv_sug2=0&prefixsug=111&rsp=1&inputT=491&rsv_sug4=491',
true,
);
console.log(obj);
复制代码
注意,这个url.parse方法 若是你要解析query的化,给第二个参数加个true就好,不然仍是一长坨字符串
好的,能够看到把我们能想到的信息差很少已经切得差很少了 而后我们再来修改一下代码
const http = require('http');
const urlLib = require('url');
http
.createServer((req, res) => {
let obj = urlLib.parse(req.url, true);
let url = obj.pathname;
let GET = obj.query;
console.log(url, GET);
res.write('success');
res.end();
})
.listen(8080);
复制代码
很简单,对不对?
POST数据跟GET稍微有点区别,由于前端无论你是怎么发过来的,只要是get请求,他确定都在url里 不过post不同,它很大,至少它能够很大,正常一个post数据也还好,可是若是你要是上传个图片,上传个音频,甚至是视频什么乱七八遭的,确定多大的都有。 这是有就有一个问题了,好比我要上传一个视频,1个g,那这个过程要怎么发,若是就是正常发的话,是否是只要有一个字节错了,或者丢包了,就要所有重来啊,这个就很低效,因此post数据是分段发送的 既然它是分段发送的,那么我们确定也要分段来接收
好的,接下来就要介绍另一个东西,在我们的 req
上有个 on
,相信熟悉前台的的你们都能反应过来,有点像事件,在这也差很少是事件的意思,后续的文章我会提到 NodeJS Events 的概念
这里就直接来写两个 一个是 data
还有一个 end
,这个data会发生不少次,至于到底多少,这得看你文件多大,这个end也很简单,只发生一次,就是数据所有发完的时候 我们来直接改造一下以前的代码
server.js
const http = require('http');
http
.createServer((req, res) => {
var str = ''; // 接收数据用的 ?
var i = 0;
req.on('data', data => {
console.log(`第${i++}次接收请求`);
str += data;
});
req.on('end', () => {
console.log(str);
});
})
.listen(8080);
复制代码
注意⚠️,而后把以前的form.html中的method 改为 POST,而后运行,提交一下数据,能够回来看控制台
提交事后之因此会一直转由于我们什么都没返回 能够看到控制台
可能这时候有人会有疑问,不是分段屡次么,这咋就一次,由于我们数据太少了,若是就这两个字他就分。。。那不是脑子有病么
若是就是想看看数据量大会怎么样,我们能够直接写一个 textarea
来试试
固然了,你们测试要有个度,要否则vscode可能受不了 像我这样直接爆炸,找不到第几回了该
最后我放弃了,vscode实在是承受不住,终端真香
固然了,你们细心的话可能看到上面代码我在那个 str的注释给了一个 ?,这是为何呢,由于我们如今是作实验,若是要是文件的化二进制确定就不行了,那么怎么办呢? 嘿嘿,我不说
放下手中的西瓜刀哈,下文就有,先别着急
固然了,我们如今拿到的数据是否是又是这一坨东西呀,好的废话很少说,直接 querystring
伺候
改造一下代码
const http = require('http');
const querystring = require('querystring');
http
.createServer((req, res) => {
var str = ''; // 接收数据用的 ?
var i = 0;
req.on('data', data => {
console.log(`第${i++}次接收请求`);
str += data;
});
req.on('end', () => {
console.log(querystring.parse(str));
});
})
.listen(8080);
复制代码
从新发请求会看到
就ok了,是否是很简单~
接下来,我们来处理一下上传文件
我们一步一步来,先来改一下 form.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>
</head>
<body>
<form action="http://localhost:8080/aaa" method="POST">
用户:<input type="text" name="user" value="" />
密码:<input type="password" name="pass" value="" />
<input type="file" name="file" value="文件" />
<input type="submit" value="提交" />
</form>
</body>
</html>
复制代码
加一个input file,而后我们直接来试试,万一行了呢,固然,哪有那么好的万一呢
emmmm,有没有感受不对劲,上传的只有文件的名字,并无任何数据,固然了,相信你们也都知道,普通的form表单是没办法直接传文件的,得加一个enctype 可能有人对这东西不太熟,这里简单介绍一下 application/x-www-form-urlencoded // 这个也就是我们日常的 name=321&pass=321这种格式的数据 multipart/form-data // 这个multipart就是分割成多个部分,由于你有可能有多个文件,form-data就是上传的是真正表单的数据 text/plain //这个就是纯文本
这么一看确定就知道了,确定是form-data 好的这里来改一下 form.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>
</head>
<body>
<form action="http://localhost:8080/aaa" method="POST" enctype="multipart/form-data" >
用户:<input type="text" name="user" value="" />
密码:<input type="password" name="pass" value="" />
<input type="file" name="file" value="文件" />
<input type="submit" value="提交" />
</form>
</body>
</html>
复制代码
这时候再提交一下,能够看到 像那么回事了对不对,固然了,这一堆东西确定也是无法直接用的
这里就要介绍一个模块了 叫 busboy
废话不说,一直接来按一下 yarn init -y
yarn add busboy
这个处理文件稍微有点复杂,我们先上代码,而后再解释
先把from.html除了上传文件的其余input删掉,由于真是项目中,具体缘由后续文章详细讲文件的和其余框架的时候会提到缘由
const http = require('http');
const querystring = require('querystring');
const Busboy = require('busboy');
http
.createServer((req, res) => {
if (req.method === 'POST') {
var busboy = new Busboy({ headers: req.headers });
busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
console.log('File [' + fieldname + ']: filename: ' + filename + ', encoding: ' + encoding + ', mimetype: ' + mimetype, 0);
file.on('data', function(data) {
console.log('File [' + fieldname + '] got ' + data.length + ' bytes', 1);
});
file.on('end', function() {
console.log('File [' + fieldname + '] Finished', 2);
});
});
busboy.on('field', function(fieldname, val, fieldnameTruncated, valTruncated, encoding, mimetype) {
console.log('Field [' + fieldname + ']');
});
busboy.on('finish', function() {
console.log('Done parsing form!');
res.end();
});
req.pipe(busboy);
}
})
.listen(8080);
复制代码
其实根据我们上面几个模块和以前的文章都看名字大概都已经能猜的八九不离十了,监听文件,分段接收,结束,车结束返回,失败了直接结束 惟一特殊点的多是那个 req.pipe,这个pipe是 NodeJS stream
里的方法,这块的话题展开就有点大了, 暂时能够简单理解为 busboy
写了文件流的操做方法,这个req.pipe把文件交给busboy
这时候能够上传一下文件而后看控制台 其实字段名字大概猜都已经能猜到了,无非就是一下名字,大小,后缀等等
好的,知道了这些api以后我们稍微删一删,而后作一个最基本的出来上传文件
首先,先建一个static目录,用于装我们上传的文件,而后改造一下代码
const http = require('http');
const querystring = require('querystring');
const Busboy = require('busboy');
const path = require('path');
const fs = require('fs');
http
.createServer((req, res) => {
if (req.method === 'POST') {
var busboy = new Busboy({ headers: req.headers });
busboy.on('file', function(fieldname, file, filename) {
var saveTo = path.join(__dirname, 'static', path.basename(fieldname));
console.log(saveTo, filename);
file.pipe(fs.createWriteStream(saveTo));
});
busboy.on('finish', function() {
res.writeHead(200, { Connection: 'close' });
res.end("That's all folks!");
});
return req.pipe(busboy);
}
res.writeHead(404);
res.end();
})
.listen(8080);
复制代码
这时候上传一下文件, 能够看到
好的,基本上已经成功了,不过如今仍是有不少不少问题,我们先来解释一下代码,而后再看问题
首先是 __dirname
这个是当前的绝对路径,一个魔术变量,你们能够直接console出来看一眼, 而后这个path.join 能够把路径拼成一个完整的绝对路径 而后这个file.pipe跟我们上面的req.pipe同样,一样是把流交给别人处理,这里是直接交给fs,来写入一个流文件 是否是很简单 而后我们来看一下问题,首先,我们这个名字,不该该直接用用户传过来的名字,由于 0.png
这种名字重复率实在是过高了,还有就是我们后续确定是应该放到公用的接口,而后把名字返回给前端,而且存到数据库里
而且,我们仅仅知道这些对文件操做仍是远远不够的,好比说,视频,发上来能够,怎么拿呢?一口气给用户返回去?这确定不行,毕竟5g还没普及呢对吧,那怎么发呢?分段?,快进什么的须要怎么操做呢?以及怎么加密等等等等
这也算是留给你们的思考,名字很简单,愿意的化能够直接用uuid来生成随机的,这个不会重复,准确的是几率足够低,fs有 rename方法,直接修改一下再存就能够
不过像multer这种库使用的 sha
这个能够在NodeJS
的crypto
里建立,按照流来生成,相同的内容永远都同样,这也符合我们的项目,相同的文件就应该直接替换了,还能减小垃圾文件
固然了,后续立刻会出express了,直接用multer也会帮你把名字处理好,并且写到如今你们应该已经感受到了,纯原生写东西太tm麻烦了,动不动就是一坨 后续会出express如何去搞这些数据,包括静态化和服务端渲染 而后再接着出koa的 最后是egg.js 搭企业级的应用
分享不易,记得点赞和关注哦,每周都会分享2~4篇文章
好的,本节node教程就写到这了,你们有什么问题欢迎在评论区评论噢 或者能够加个人qq和微信,我们一块儿沟通 qq:
916829411
复制代码
微信:
Dyy916829411
复制代码