摘要:文件用來(lái)路由不同的服務(wù)器的有三個(gè)功能所以包含有三個(gè)模塊兒,開頭就引入了三個(gè)模塊兒,通過請(qǐng)求的路徑名稱我們路由到不同的處理模塊兒。鏈接到一個(gè)真實(shí)的服務(wù)器進(jìn)行域名解析,且始終使用網(wǎng)絡(luò)進(jìn)行查詢。
Node搭建DNS服務(wù)器的過程
接下來(lái)請(qǐng)深呼吸一大片代碼正奔涌而來(lái),該項(xiàng)目托管在https://github.com/MaxMin-she... 請(qǐng)各位同仁大神view指導(dǎo)。
1、route文件用來(lái)路由不同的action
const DNSserver = require("./src/controller/DNSserver.js"); const Staticfiels = require("./src/controller/Staticfiels.js"); const SaveImg = require("./src/controller/Saveimg.js") exports.getRouter = function (req, res) { console.log(url.parse(req.url)); const pathname = url.parse(req.url).pathname; switch (pathname) { case "/dns": DNSserver.parse(req, res); break; case "": case "/": case "/index": Staticfiels.index(req, res); break; case "/post/img": SaveImg.saveimg (req, res); break; default: Staticfiels.loadfiels(req, res, pathname); } } module.exports = exports;
DNS服務(wù)器的有三個(gè)功能所以包含有三個(gè)模塊兒,開頭就引入了三個(gè)模塊兒,通過請(qǐng)求的url路徑名稱我們路由到不同的處理模塊兒。這個(gè)簡(jiǎn)易的DNS服務(wù)器總共有四個(gè)自定義的模塊:
utile: 自定義的錯(cuò)誤處理模塊兒
DNSserver:進(jìn)行DNS域名解析,獲取域名所對(duì)應(yīng)的IP地址
Staticfiles: 根據(jù)請(qǐng)求路徑加載靜態(tài)文件
Saveimg: 存儲(chǔ)圖片,返回一個(gè)自定的存儲(chǔ)路徑
接下來(lái),我們分別來(lái)介紹這幾個(gè)模塊兒的功能和作用:
utile模塊兒/** * handdle error * @param err * @param msg */ exports.errorHandle = function(err, type){ const time = new Date(); console.log(`------------------------ time: ${time} err: ${err} type: ${type} ------------------------ `); } module.exports = exports;
該模塊兒的作用是通過傳入的err,和提示的msg將錯(cuò)誤的結(jié)果打印出來(lái)
DNSserver模塊兒/** * DNS解析 */ const url = require("url"); const querystring = require("querystring"); const dns = require("dns"); const util = require("../utile/utile.js"); /** * @param req * @param res */ exports.parse = function(req, res){ const query_url = url.parse(req.url); const query = querystring.parse(query_url.query); dns.resolve4(query["hostname"], function(err, addresses){ if(err){ util.errorHandle(err, "DNS failed"); res.writeHead(400); res.end(); } else { res.writeHead(200); res.end(addresses.toString()); } }); } module.exports = exports;
req.url:req.url是一個(gè)包含著請(qǐng)求基本信息的字符串,以‘http://user:[email protected]:8080/path?query=string#hash’為例,主要包含的屬性字段有:
1、protocal:‘http’,協(xié)議類型
2、slashes:true,表示protocal冒號(hào)后面跟著兩個(gè)ASCII 斜杠字符
3、auth:"user:pass",由username:user和password:pass組成
4、host:"host.com:8080",由hostname(域名或者ip地址)和port(端口號(hào))8080組成
5、path:‘/path?query=string’,路徑,是由pathname(路徑名稱:‘/path’)和search(查詢名稱:‘?query=string’)組成
6、query:‘query=string’,由搜索對(duì)象形成
url.parse():url模塊的parse方法是將上面所說(shuō)的這些屬性值序列化成鍵值對(duì)對(duì)像。
假設(shè)我們的請(qǐng)求是:‘http://localhost:3000/dns?hostname=www.google.com’
querystring.parse(str[, sep[, eq[, options]]]):query的屬性的值類似于"hostname=www.google.com"這樣的值,querystring(查詢字符串)模塊兒的作用是用來(lái)解析和格式化url查詢字符串,其中的parse方法是將這種形式的字符串序列化成{hostname:google.com}這樣的鍵值對(duì)集合。
str:需要分割的查詢字符串
sep:用于界定查詢字符串中鍵值對(duì)的符號(hào)
eq:用于界定查詢字符串中鍵與值的符號(hào)
options:用來(lái)定義解碼查詢字符串的函數(shù)和解析鍵的最大數(shù)量
dns.resolve4(str,function(err, ad){}):dns(域名服務(wù)器模塊兒),這個(gè)模塊包含兩種函數(shù):1、使用底層操作系統(tǒng)工具進(jìn)行域名解析,無(wú)需進(jìn)行網(wǎng)絡(luò)通訊。2、鏈接到一個(gè)真實(shí)的DNS服務(wù)器進(jìn)行域名解析,且始終使用網(wǎng)絡(luò)進(jìn)行查詢。resolve4()屬于第二種函數(shù)。它的作用是使用DNS協(xié)議解析IPV4地址主機(jī)名,回調(diào)函數(shù)中的第一個(gè)參數(shù)是出現(xiàn)的錯(cuò)誤,第二個(gè)參數(shù)是解析得到的ip地址,注意:這里返回的addresses是一個(gè)IPV4地址數(shù)組,但是res.end()的數(shù)據(jù)類型只能是string或者buffer,所以在響應(yīng)是需要回調(diào)toString方法,將數(shù)組轉(zhuǎn)化成字符串。
Staticfiles模塊兒/** * get static files */ const fs = require("fs"); const path = require("path"); const util = require("../utile/utile.js"); /** * read Fiels * @param req * @param res * @param pathname */ const readStaticFiles = function(req, res, filename){ fs.readFile(filename, function(err, data){ if(err){ util.errorHandle(err, "filed readFile"); res.writeHead(404); res.end("We Got A Problem: File Not Found"); } else { res.writeHead(200); res.end(data); } }) } /** * exports function of reading files */ exports.loadfiels = function(req, res, pathname){ const filename = path.join("E:static", pathname); console.log(filename); readStaticFiles(req, res, filename); } module.exports = exports; /** * exports function of getting default page */ exports.index = function(req, res){ const filename = path.join("static", "html/index.html"); readStaticFiles(req, res, filename); }
Staticfiles文件中有兩個(gè)輸出,index模塊是用來(lái)處理沒有輸入文件名時(shí)的默認(rèn)值,loadfiels模塊則可以根據(jù)文件名返回靜態(tài)文件,兩個(gè)模塊兒都使用的同一函數(shù)readStaticFiles進(jìn)行文件的讀取操作。
path模塊兒:用來(lái)處理文件和目錄的路徑
path.join([...paths]):將給定的所有path片段使用平臺(tái)特定的鏈接符鏈接成規(guī)范化路徑。在這個(gè)項(xiàng)目中,由于所有靜態(tài)文件都放在該項(xiàng)目的static目錄下面。所以,請(qǐng)求路徑之前要加一個(gè)相對(duì)路徑"static",不然就會(huì)報(bào)路徑錯(cuò)誤的error。
fs.readFile(path[,options],callback):根據(jù)路徑異步讀取文件,回調(diào)函數(shù)中返回兩個(gè)參數(shù):第一個(gè):error是讀取文件過程中產(chǎn)生的錯(cuò)誤,第二個(gè):data是讀取文件的二進(jìn)制數(shù)據(jù)流,如果在option中未指定編碼方式,返回的則是一個(gè)原始的buffer。
Saveimg模塊兒在這個(gè)模塊中我們將實(shí)現(xiàn)圖片上傳下載的功能。
首先在html中完成一個(gè)form表單:
"multipart/form-data"是post的一種數(shù)據(jù)提交方式,用于附件的上傳,表單中還有file類型的控件,用于上傳一張圖片:
接下來(lái),我們了解一下請(qǐng)求報(bào)文頭和報(bào)文體的格式和內(nèi)容:
請(qǐng)求報(bào)文頭 req.headers,如下圖所示:
在請(qǐng)求報(bào)文頭中可以找到這些信息,其中Content-Type中的boundary屬性很重要,因?yàn)楦郊臄?shù)據(jù)量比較大,所以一個(gè)附件需要多部分提交才能完成,而boundary就是每一部分內(nèi)容之間的分隔符;Content-Length是報(bào)文的長(zhǎng)度。
報(bào)文體如下所示:
------WebKitFormBoundaryKXd7iAk5VsWqoaAY Content-Disposition: form-data; name="userfile1"; filename="2.jpg" Content-Type: image/jpeg ------WebKitFormBoundaryKXd7iAk5VsWqoaAY--
因?yàn)閭鬏數(shù)臄?shù)據(jù)量是未知的,所以通過boundary處理報(bào)文體是至關(guān)重要的一步。
在了解完附件上傳的報(bào)文形式以后,接下來(lái)我們將一步步的來(lái)實(shí)現(xiàn)圖片上傳的所有功能:
exports.saveimg = function (req, res) { if (req.method.toLowerCase() === "get") { getHandle(req, res); } else if (req.method.toLowerCase() === "post") { postHandle(req, res); } }
首先,我們通過請(qǐng)求的方式來(lái)進(jìn)行分支處理,上傳圖片的http請(qǐng)求方式必須是post,postHandle函數(shù)的具體實(shí)現(xiàn)過程如下:
function postHandle(req, res) { req.setEncoding("binary"); let body = ""; let filename = ""; req.on("data", function (chunk) { body += chunk; }); req.on("end", function () { const boundary = req.headers["content-type"].split(";")[1].replace("boundary=", ""); (1) const file = querystring.parse(body, " ", ":"); (2) if (file["Content-Type"].indexOf("image") !== -1) { const fileAr = file["Content-Disposition"].split("; ")[2].replace("filename=", "").split(".");(3) let filename = fileAr[0];(4) const imageState = fileAr[1].substring(0, fileAr[1].length-1);(5) const entireData = body.toString(); const contentType = file["Content-Type"].substring(1); const upperBound = entireData.indexOf(contentType) + contentType.length;(6) const tarStr = entireData.substring(upperBound).trim(); const boundaryIndex = tarStr.length - boundary.length - 4; const binaryData = tarStr.substring(0, boundaryIndex); //重新設(shè)置文件名稱 filename = randomImgString(filename); fs.writeFile(path.join(__dirname, `../../img/${filename}.${imageState}`), binaryData, { encoding: "binary" }, (err) => { if (err) { utile.errorHandle(err, "failed write file"); } else { res.writeHead(200, { "Content-Type": "application/json" }); const data = JSON.stringify({ "url":`http://127.0.0.1:3000/${filename}.${imageState}` }) console.log(data); res.write(data); res.end(); } }); } }) }
req.on("data",callback)綁定了用來(lái)監(jiān)聽數(shù)據(jù)流的事件,req.on("end",callback)監(jiān)聽數(shù)據(jù)傳輸完畢的事件,由此可見對(duì)傳輸來(lái)的數(shù)據(jù)進(jìn)行的一系列操作都應(yīng)該放在這個(gè)監(jiān)聽事件的回調(diào)函數(shù)當(dāng)中
body變量中存儲(chǔ)的是本次附件上傳中存儲(chǔ)的所有數(shù)據(jù),如下圖所示(我只截取了body變量中的一部分):
以下截圖是body的開頭部分:
以下截圖則是body的結(jié)束部分:
(1)段代碼的作用是從請(qǐng)求報(bào)文頭的content-type屬性值中截取boundary分割符的內(nèi)容
(2)段代碼的作用是提取出報(bào)文體中的鍵值對(duì),querystring模塊兒中的parse方法上文有提及,解析后的具體內(nèi)容如下圖所示:
這段代碼的目的是為了獲取到報(bào)文體中的Content-Disposition字段和Content-Type字段,從Content-Disposition字段中可以獲取到文件名稱和文件格式,代碼3,4,5則完成了這個(gè)功能。
從打印的返回的報(bào)文體來(lái)看,Content-Type以后的所有數(shù)據(jù)就是圖片的編碼,所以接下來(lái)的任務(wù)就是將這個(gè)編碼提取出來(lái)
(6)段代碼的作用是找到圖片編碼字符串開始的index
(7)段代碼的作用是找到圖片編碼字符串的結(jié)束index,由body的結(jié)尾截圖可以看出,結(jié)束部分是由‘--boundary--’的形式組成,所以最后減去的除了boundary的長(zhǎng)度還有兩個(gè)‘--’的長(zhǎng)度4。
(8)段代碼中的binaryData則是圖片的完整編碼
隨機(jī)生成文件名稱的函數(shù)randomImgString的實(shí)現(xiàn)過程如下所示:
/** * option to generate randomString */ function randomImgString(filename){ let outString = new Date().toTimeString(); outString += filename.substring(0, filename.indexOf(".")); outString = hash.update(outString) .digest("hex").substring(0, 15); return outString; }
*fs.writeFile(file, data[, options], callback):1、file:文件的存儲(chǔ)路徑 2、data:文件編碼 3、options編碼方式 4、callback:寫入文件成功后的回調(diào)函數(shù)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/92898.html
摘要:?jiǎn)栴}描述在搭建集群過程中,安裝了插件后,運(yùn)行一個(gè)容器,發(fā)現(xiàn)容器內(nèi)無(wú)法解析集群外域名,一開始可以解析集群內(nèi)域名,一段時(shí)間后也無(wú)法解析集群內(nèi)域名??偨Y(jié)通過對(duì)問題的探究,也理解了集群中解析的完整過程,如圖。 showImg(https://segmentfault.com/img/remote/1460000015639330); 問題描述 在搭建Kubernetes集群過程中,安裝了kub...
摘要:?jiǎn)栴}描述在搭建集群過程中,安裝了插件后,運(yùn)行一個(gè)容器,發(fā)現(xiàn)容器內(nèi)無(wú)法解析集群外域名,一開始可以解析集群內(nèi)域名,一段時(shí)間后也無(wú)法解析集群內(nèi)域名??偨Y(jié)通過對(duì)問題的探究,也理解了集群中解析的完整過程,如圖。 showImg(https://segmentfault.com/img/remote/1460000015639330); 問題描述 在搭建Kubernetes集群過程中,安裝了kub...
摘要:手動(dòng)搭建集群探索系列的第三篇,主要記錄手動(dòng)搭建集群的過程,部署部署用作服務(wù)發(fā)現(xiàn)。配置的子網(wǎng)范圍不能和的一致。 手動(dòng)搭建kubernetes集群 探索kubernetes系列的第三篇,主要記錄手動(dòng)搭建k8s集群的過程,部署dashboard, 部署DNS用作服務(wù)發(fā)現(xiàn)。順便記錄一下k8s中的一些資源的概念。 配置環(huán)境 這個(gè)步驟可以參考《Flannel with Docker》文中的步驟,不...
摘要:?jiǎn)栴}是不是定義的一個(gè)的容器集群是只部署在同一個(gè)主機(jī)上楊樂到目前是,同一個(gè)里的是部署在同一臺(tái)主機(jī)的。問題這個(gè)圖里的是安裝在哪里的所有的客戶端以及會(huì)連接這個(gè)嘛楊樂可以任意地方,只要能訪問到集群,會(huì)作為的出口。 kubernetes1.0剛剛發(fā)布,開源社區(qū)400多位貢獻(xiàn)者一年的努力,多達(dá)14000多次的代碼提交,最終達(dá)到了之前預(yù)計(jì)的milestone, 并意味著這個(gè)開源容器編排系統(tǒng)可以正式在...
閱讀 2113·2021-11-18 10:02
閱讀 2863·2021-09-04 16:41
閱讀 1155·2019-08-30 15:55
閱讀 1420·2019-08-29 17:27
閱讀 1105·2019-08-29 17:12
閱讀 2539·2019-08-29 15:38
閱讀 2864·2019-08-29 13:02
閱讀 2841·2019-08-29 12:29