node --- 游走在客户端和服务器间的http
本篇文章,講述了一個很簡單的上傳圖片(/start)到本地服務(wù)器,然后路由跳轉(zhuǎn)到/upload.
寫這個程序的目的是為了幫助理解HTTP的一些基本概念及node對于http api的實現(xiàn)以及程序的設(shè)計模式.
IP: 計算機之間的通信
TCP: 應(yīng)用程序之間的通信
HTTP: 基于TCP實現(xiàn)的應(yīng)用層協(xié)議,設(shè)計之初是為了提供一種HTML頁面的發(fā)布和接收的方法,就是傳輸一些超文本(HyperText)的規(guī)則.
當在瀏覽器中輸入 http://localhost:8888/start 時,顯示如下內(nèi)容(有點丑,沒寫CSS…將就看吧):
選擇文件:
點擊Upload file
要解決如下幾個問題:
1.服務(wù)器監(jiān)聽localhost,端口8888下,路徑為/start的url
2.服務(wù)器處理該請求,并返回內(nèi)容(通過http)
3.返回的內(nèi)容對選擇文件和Upload File做出反應(yīng)
在此之前,需要做如下的準備: 根據(jù)依賴注入的思想,我們需要一個index.js文件來處理服務(wù)器和路由之間的關(guān)系.
index.js: 負責(zé)啟動服務(wù)器,并向服務(wù)器傳入路由和事件處理程序(事件處理程序可以參考《JavaScript高級程序設(shè)》P345)
// index.js var server = require('./server'); // 服務(wù)器,監(jiān)聽瀏覽器發(fā)送的http請求 var route = require('./route'); // 路由,對不同路徑進行不同的處理 var requestHandlers = require('./requestHandlers'); // 事件處理程序// 在index這一層,可以將requestHandlers做一些簡單的處理 // 將事件處理程序?qū)?yīng)到handle中,路徑作為屬性 var handle = {}; handle["/"] = requestHandlers.start; // 如果值輸入start就調(diào)用start的事件處理器 handle["/start"] = requestHandlers.start; handle["/uoload"] = requestHandlers.uoload; handle["/show"] = requestHandlers.show; // 調(diào)用啟動服務(wù)器.. server.start(route, handle);server服務(wù)器: 監(jiān)聽端口8888(會根據(jù)瀏覽器的請求HTTP生成一個請求路徑),將請求路徑(request.url)、求報文(resquest)、響應(yīng)報文(response)、以及事件處理程序交給路由層
// server.js var http = require('http');function start(route, handle) {// 服務(wù)器的事件處理程序 onRequestfunction onRequest(request , response) {var pathname = request.url;// 因為會多請求一個/favicon.ico(具體原因百度) , 故過濾掉它if (pathname === '/favicon.ico' ) {// 過濾掉...} else { // 其他路由// 將請求路徑交給路由層route(handle, pathname, response, request);}}// 創(chuàng)建服務(wù)器http.createServer(onRequest).listen(8888); }// 到處start方法,供其他js使用 exports.start = start;// 注:在JS中函數(shù)可以作為參數(shù),可以作為返回值,還可以作為變量賦值.具體可以參考函數(shù)式編程route層: 根據(jù)服務(wù)器傳入的檢查事件處理程序中是否含有該路徑,如果有則交給請求處理層,否則返回404響應(yīng)報文
// route.js function route(handle, pathname, response, request) {if( typeof handle[path] === 'function') {handle[pathname](response, request);} else {response.writeHead(404,{'Content-Type' : "text/plain" })response.write('404 Not Found'); // 可以自定義html的404頁面返回response.end();} } exports.route = route;請求處理層(requestHandlers): 對不同路勁的具體處理方式:start、upload、show。(后續(xù)可以根據(jù)需求拓展)
先分開來說明具體需求和實現(xiàn),最后匯總
1.start: 顯示一個上傳的input框,和一個提交的按鈕
// 注:上傳的框引入了一個外部模塊formidable 具體了解formidable
2.upload: 用于處理start頁面?zhèn)魅氲膱D片,將圖片放入/temp文件夾下.重新命名為test.png,并將圖片作為http響應(yīng)報文的body部分傳遞給瀏覽器.瀏覽器根據(jù)img的src 訪問找到圖片顯示出來.
function upload(request , response) {var form = new formidable.IncomingForm();// 這里涉及到一個cross-device問題.在結(jié)尾的參考文獻中會給出form.uploadDir = "temp";form.parse(request, (err, fields, files) => {fs.renameSync(files.upload.path, "/temp/test.png");response.writeHead(200,{ 'Content-Type' : 'text/html'});response.write("received image:<br /> ");response.write("<img src='/show' />");response.end();}) }3.show: 用于將本地的/temp/test.png顯示在瀏覽器上.觸發(fā)條件是<img src=’/show’ />.即觸發(fā)了(/show路由,但是在請求url中還是localhost:8888/upload)
function show (request, response) {let url = "./temp/test.png";fs.readFile(url, 'binary', (err, file) => {if(err ) {response.writeHead(500, {'Content-Type' : 'text/plain'});response.write(err + '\n' );response.end();} else {response.writeHead(200, {'Content-Type' : 'image/jpg' }); // 告訴瀏覽器是個圖片類型response.write(file, 'binary');response.end();}}) }1+2+3 = requestHandlers.js:
// requestHandlers.js (這樣寫的可維護性,拓展性更高) var fs = require('fs'),formidable = require('formidable');// 根據(jù)url獲取本地的html(buffer) function getHTML(url) {let promise = new Promise((resolve, reject) => {fs.readFile(url, (err, data) => {if (!err) {resolve(data)} else {reject(data)}})})return promise; }function start(response) {let url = './start.html';let html = getHTML(url);html.then((body) => {response.writeHead(200, { 'Content-Type': 'text/html' });response.write(body);response.end();}, (err) => {console.log(err);}) }function upload(response, request) {var form = new formidable.IncomingForm();form.uploadDir = 'temp';form.parse(request, (err, fields, files) => {fs.renameSync(files.upload.path, "./temp/test.png");response.writeHead(200, { "Content-Type": "text/html" });response.write("received image:<br />");response.write("<img src='/show' />");response.end();}) }function show(response) {let url = "./temp/test.png";fs.readFile(url, "binary", (err, file) => {if (err) {response.writeHead(500, { "Content-Type": "text/plain" });response.write(err + '\n');response.end();} else {response.writeHead(200, { "Content-Type": "image/jpg" });response.write(file, "binary");response.end();}}) }exports.start = start; exports.upload = upload; exports.show = show;start.html
<!DOCTYPE html> <html><head><meta http-equiv="Content-Type" content="text/html" charset="utf-8"> </head><body><!-- 使用post方法將表單中的元素提交到相對路徑下的upload中 --><form action="/upload" method="post" enctype="multipart/form-data"><input type="file" name="upload"><input type="submit" value="Upload file" /></form> </body></html>參考1: fs.renameSync報錯的問題
參考2: Node入門
總結(jié)
以上是生活随笔為你收集整理的node --- 游走在客户端和服务器间的http的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OC 教程 极光推送
- 下一篇: 算法 --- 判断某个值是否在二叉搜索