node --- 游走在客戶端和服務器間的http

本篇文章,講述了一個很簡單的上傳圖片(/start)到本地服務器,然后路由跳轉到/upload.
寫這個程序的目的是為了幫助理解HTTP的一些基本概念及node對于http api的實現以及程序的設計模式.
IP: 計算機之間的通信
TCP: 應用程序之間的通信
HTTP: 基于TCP實現的應用層協議,設計之初是為了提供一種HTML頁面的發布和接收的方法,就是傳輸一些超文本(HyperText)的規則.

// 注:一些用到的基礎,會在對應的代碼前說明。下面先簡單的介紹一下功能

當在瀏覽器中輸入 http://localhost:8888/start 時,顯示如下內容(有點丑,沒寫CSS…將就看吧):
在這里插入圖片描述
選擇文件:
在這里插入圖片描述
在這里插入圖片描述
點擊Upload file
在這里插入圖片描述

要解決如下幾個問題:
1.服務器監聽localhost,端口8888下,路徑為/start的url
2.服務器處理該請求,并返回內容(通過http)
3.返回的內容對選擇文件和Upload File做出反應

在此之前,需要做如下的準備: 根據依賴注入的思想,我們需要一個index.js文件來處理服務器和路由之間的關系.

index.js: 負責啟動服務器,并向服務器傳入路由和事件處理程序(事件處理程序可以參考《JavaScript高級程序設》P345)

// index.js
var server = require('./server');   // 服務器,監聽瀏覽器發送的http請求
var route = require('./route');    // 路由,對不同路徑進行不同的處理
var requestHandlers = require('./requestHandlers');    // 事件處理程序// 在index這一層,可以將requestHandlers做一些簡單的處理
// 將事件處理程序對應到handle中,路徑作為屬性
var handle = {};
handle["/"] = requestHandlers.start;    // 如果值輸入start就調用start的事件處理器
handle["/start"] = requestHandlers.start;   
handle["/uoload"] = requestHandlers.uoload;
handle["/show"] = requestHandlers.show; // 調用啟動服務器..
server.start(route, handle);

server服務器: 監聽端口8888(會根據瀏覽器的請求HTTP生成一個請求路徑),將請求路徑(request.url)、求報文(resquest)、響應報文(response)、以及事件處理程序交給路由層

// server.js
var http = require('http');function start(route, handle) {// 服務器的事件處理程序 onRequestfunction onRequest(request , response) {var pathname = request.url;// 因為會多請求一個/favicon.ico(具體原因百度) , 故過濾掉它if (pathname === '/favicon.ico' ) {// 過濾掉...} else {  // 其他路由// 將請求路徑交給路由層route(handle, pathname, response, request);}}// 創建服務器http.createServer(onRequest).listen(8888);
}// 到處start方法,供其他js使用
exports.start = start;// 注:在JS中函數可以作為參數,可以作為返回值,還可以作為變量賦值.具體可以參考函數式編程

route層: 根據服務器傳入的檢查事件處理程序中是否含有該路徑,如果有則交給請求處理層,否則返回404響應報文

// 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。(后續可以根據需求拓展)

先分開來說明具體需求和實現,最后匯總
1.start: 顯示一個上傳的input框,和一個提交的按鈕
// 注:上傳的框引入了一個外部模塊formidable 具體了解formidable

// start(這是講解,后面匯總)
// start 會顯示一個html頁面,可以使用fs來寫一個讀取Html頁面的函數getHTML
function getHTML(url) {let promise = new Promise(resolve, reject) {fs.readFile(url, (err, data) {if (!err) {resolve(data);} else {reject(err)}})return promise
}// 可以使用上面定義函數寫start函數
function start(request, response) {let url = "./start.html"  // 最后會給出let html = getHTML(url);html.then((data) => {response.writeHead(200,{'Content-Type' : 'text/plain'});response.write(data);  // 可以接收buffer , stringresponse.end();}, (err) => {console.log(err);})
}

2.upload: 用于處理start頁面傳入的圖片,將圖片放入/temp文件夾下.重新命名為test.png,并將圖片作為http響應報文的body部分傳遞給瀏覽器.瀏覽器根據img的src 訪問找到圖片顯示出來.

function upload(request , response) {var form = new formidable.IncomingForm();// 這里涉及到一個cross-device問題.在結尾的參考文獻中會給出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顯示在瀏覽器上.觸發條件是<img src=’/show’ />.即觸發了(/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');// 根據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入門

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/251010.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/251010.shtml
英文地址,請注明出處:http://en.pswp.cn/news/251010.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

BigDecimal踩過的大坑

通常Java中涉及金錢相關的計算為了保持精度&#xff0c;會采用BigDecimal來實現&#xff0c;但是BigDecimal中創建BigDecimal類對象的時候&#xff0c;如果使用直接new的話&#xff0c;必須是String類型的參數&#xff0c;否則會導致創建出來的對象不是你想要的&#xff0c;比如…

redis配置環境變量

直接上圖不解釋 redis安裝路徑&#xff0c;我的電腦右擊屬性 窗口R鍵&#xff0c;輸入cmd回車&#xff0c;輸入redis-server.exe 回車 再開一個命令窗口&#xff0c;窗口R鍵&#xff0c;輸入cmd回車&#xff0c;輸入 redis-cli.exe -h 127.0.0.1 -p 6379 回車 轉載于:https:/…

對轉義字符的思考

ASCII碼 計算機存儲文字時用的是二進制&#xff0c;ASCII碼就是一張對照表&#xff0c;什么字符對應什么碼&#xff0c;將二進制碼存儲下來0-127位表示基礎的ASCII碼0-31&#xff0c;和127表示非打印控制字符&#xff08;如換行、回車、響鈴、文頭、文尾&#xff09;32-126表示…

進程總結

進程總結 進程&#xff1a; 正在進行的一個過程或者說一個任務 進程是計算機中資源分配的最小單位 多進程之間的數據是隔離的 多進程是用來解決高計算型的程序用的 啟動進程的開銷比較大&#xff0c;其開啟數量和cpu的個數相關&#xff0c;正常在cpu的個數1-2倍之間 進程越多&a…

debian如何安裝Let's Encrypt

如果python默認版本不是2&#xff0c;刪除 /usr/bin/python 添加軟引用 in -s /usr/bin/python2 /usr/bin/python 第一步&#xff0c;卸載virtualenv apt-get purge python-virtualenv python3-virtualenv virtualenv 如果pip沒有就安裝pip apt-get install python-pip pip …

算法 --- 判斷某個值是否在二叉搜索樹中

function funA(root, k) {if(root null) {return false} else {if(k root.val) {return true } else {if( k < root.val) {return funA(root.left, k)} else {return funA(root.right, k)}}} }

ES6-12 array/數值拓展、ArrayOf、ArrayFrom

要使用…須有迭代器接口 數組方法 構造器上的方法 Array.of()聲明數組 替代new Array()的方式聲明數組new Array()不傳參數返回空數組&#xff0c;只傳1個參數時&#xff0c;代表數組長度&#xff0c;內容用empty填充&#xff0c;傳多個參數&#xff0c;則代表數組內容&…

React01

目錄 React-day01 入門知識React介紹官網React開發環境初始化 SPA腳手架初始化項目&#xff08;方便&#xff0c;穩定&#xff09;*通過webpack進行初始化配置鏡像地址開發工具配置元素渲染組件及組件狀態函數定義組件(無狀態)類定義組件(有狀態)*組合組件Props屬性*State狀態*…

算法 --- 反轉數組

幾個注意點: 1.輸出的時候,也要做數字超出處理 2.js中可以使用 str -0 將字符串類型轉換成數字類型 ( 注意不是 0) 3.可以使用 num ‘’ 將數字類型轉換成字符串類型 4.使用str.split(’’) 可以將字符串轉換成數組 5.使用arr.join(’’) 可以將數組轉換成字符串 6.JS中2的31次…

ES6-13 正則方法、修飾符yus、UTF_16編碼方式

修飾符 m multiLine 對于str中含\n的情況g globali ignoreCase 元字符 反斜杠加轉義 元字符含義簡寫\w匹配字母、數字、下劃線。等價于’[A-Za-z0-9_]’。word\W匹配非字母、數字、下劃線。等價于 ‘[^A-Za-z0-9_]’。\s匹配任何空白字符&#xff0c;包括空格、制表符、換頁…

svn文件大小類型限制,提交必須加多少字的說明

#!/bin/shREPOS"$1" TXN"$2" #此處更改大小限制&#xff0c;這里是5M MAX_SIZE5242880 #此處增加限制文件后綴名 FILTER\.(zip|rar|o|obj|tar|gz)$SVNLOOK/usr/bin/svnlookLOGMSG$SVNLOOK log -t "$TXN" "$REPOS" | wc -cif [ "$…

cmd窗口快速定位到具體文件夾方法

在用Python進行機器實戰時&#xff0c;打開cmd窗口后&#xff0c;總是到定位到kNN.py所在文件夾才能Python&#xff08;否則import kNN失敗&#xff09;&#xff0c;每次都要輸入地址非常麻煩 這里介紹一個cmd窗口快速定位到具體文件夾方法&#xff1a; 按住Shift鍵右擊鼠標打開…

算法 --- 羅馬數字轉整數

解體思路: 1.寫一個對象trans用于保存羅馬和數字之間的映射關系 2.重點在于當數值小的出現在數值大的左邊時,會減去該數,出現在右邊時會加上該數,因此需要與后面的進行比較 3.在得到s時,首先給它轉換成字符串,并在末位加一個0 /*** param {string} s* return {number}*/ var r…

正則 - 阮一峰

學習地址 一 正則實例方法 1. test 正則實例.test返回布爾值 var r /x/g; var s _x_x;r.lastIndex // 0 r.test(s) // truer.lastIndex // 2 r.test(s) // truer.lastIndex // 4 r.test(s) // false死循環&#xff0c;因為while循環的每次匹配條件都是一個新的正則表達式…

LintCode 6.合并排序數組 ||

import org.junit.Test;import java.util.Arrays;public class MergeSort {/*** param A: sorted integer array A* param B: sorted integer array B* return: A new sorted integer array* <p>* 合并排序數組 II* 合并兩個排序的整數數組A和B變成一個新的數組。* <p…

算法 --- 有效的括號

解題思路: 1.對括號分別賦值(左括號大于0,右括號小于0),方便后期比較 2.使用棧,對于大于0的字符串入棧,對于小于0的字符串,檢查棧中是否有元素,若沒有返回false,否則拿出棧頂的一個元素,和現在的元素進行比較 /** * param {string} s * return {boolean} */ var isValid func…

ES6-14 Unicode表示法、字符串方法、模板字符串

Unicode表示法 本身能正常識別的&#xff0c;加{}也能識別&#xff0c;花括號內的內容表示碼點 parseInt(0061,16) // 十進制的97&#xff0c;97在ASCII碼中對應a console.log(\u0061) // a console.log(\u{0061}) // a console.log(\u{61}) // a原型上方法 codePointAt(十進…

對 js 高程 Preflighted Reqeusts 的理解

看JS高程遇到 Preflighted Reqeusts不大理解&#xff0c;遂百度下&#xff1a;轉自&#xff1a;http://todoit.me/ajax-preflight/最近在做一個 VUE 的項目的時候, 和后端的小伙伴對接口, 想方便開發, 于是要求后端的小伙伴在所有的接口都加上跨域的許可 (Access-Control-Allow…

算法 --- 刪除數組中重復項

解題思路: 如果輸入的數組長度為1,則返回該數組否則(len>2),使用i記錄當前待插入的位置,j記錄下一個與nums[i]不相等的位置,leng為待返回數組的長度當nums[i] ! nums[j]時,把j位置的值nums[j]放在i1位置.同時i,j /*** param {number[]} nums* return {number}*/ var remov…

如何理解 Linux 中的 load averages

原文&#xff1a;https://mp.weixin.qq.com/s?src11&timestamp1533697106&ver1047&signaturepoqrJFfcNABv4biKKpa4mZdIW7No2Wo1F5sbZL7ggoVS2GqcSqwQQ8hMulAmezT*zL*klB-eE5BeMyNuyjuIH7YgkBAN25i6*ahhEpWyxqx6vPct-Vr7q7AU0YGe-F*l&new1 http://blog.scoutap…