在 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
複製代碼