nodeJs學習過程之一個圖片上傳顯示的例子

目標

1. 在瀏覽器地址欄輸入「http://demos/start」,進入歡迎頁面,頁面有一個文件上傳表單;javascript

2. 選擇一張圖片並提交表單,文件被上傳到"http://demos/uploads"上傳完成把該圖片顯示在頁面上。php

功能模塊分解

1. 須要提供歡迎頁,因此須要一個http服務器;html

2. 對於不一樣請求,根據url,服務器能給與不一樣響應,須要路由,把請求對應到相應的請求處理程序(request handler)java

3. 須要請求處理程序;node

4. 路由處理post數據,並把數據封裝成更加友好的格式傳遞給處理程序,須要數據處理功能;web

5. 須要視圖邏輯供請求處理程序使用,以便將結果返回給瀏覽器;ajax

6. 須要上傳處理功能處理上傳細節。shell

若是是php實現,咱們須要apache http服務器並配置mod_php5模塊,那麼整個「接收請求並回復「並不須要php來作。而使用nodejs,不只僅是實現應用,同時還實現了http服務器。數據庫

一個基礎的http服務器

nodejs支持模塊化,有利於編寫乾淨的代碼。除了內置的模塊,咱們能夠將本身的不一樣功能的代碼放進不一樣模塊中,說白了就是寫在不一樣文件中。apache

建立js文件取名server.js,寫入代碼以下:

1 var http = require('http');
2 
3 http.createServer(function(req,res){
4   res.writeHead(200,{'Content-Type':'text/html'});
5   res.write('<h1>Node.js</h1>');
6   res.end("");
7 }).listen(3000,'127.0.0.1');
node server.js

瀏覽器輸入"http://localhost:3000",顯示」Node.js「。

服務器簡單分析

第一行,require請求nodejs內置的http模塊,賦給http變量;接着調用http模塊的createServer方法,返回一個對象,這個對象有一個叫作listen的方法,這個方法的參數指定http服務器監聽的端口號。createServer方法的匿名函數是該方法僅僅須要的一個參數,js中函數和變量同樣能夠傳遞。

使用這種方式是由於nodejs是事件驅動的。

當咱們使用php時,任什麼時候候每當有請求進入,apache就爲這一新請求新建一個進程,並從頭至尾的執行php腳本。htpp.createServer不只僅是建一個偵聽某端口的服務器,咱們還想要他在收到http請求時作點什麼。而nodejs是異步的:請求任什麼時候候均可能到達,可是他卻跑在一個單進程中。

當一個請求到達3000端口時怎麼控制流程呢?這時候nodejs/javascript的事件驅動機制派上用場了。

咱們向建立服務器的方法傳遞了一個函數,任什麼時候候服務器收到請求,這個函數就會被調用,被用做處理請求的的地方。

服務器如何處理請求

當服務器加收請求回調的匿名函數被觸發的時候,有兩參數傳入:request和response。利用這兩個對象的方法處理請求的細節,並返回應答。

使用response.writeHead()發送一個http狀態200和http頭的內容類型等,使用response.write()在響應主體中發送文本,而使用response.end()完成應答。

封裝爲模塊:

 1 var http = require('http');
 2 
 3 function serverStart(){
 4         http.createServer(function(req,res){
 5             console.log('http request recieve');
 6             
 7           res.writeHead(200,{'Content-Type':'text/html'});
 8           res.write('<h1>Node.js</h1>');  
 9           res.end("");
10         }).listen(3000,'127.0.0.1');
11         
12         console.log('http server start on port 3000');
13 }
14 
15 exports.serverStart = serverStart;

第一行http是內置的模塊,這裏封裝了咱們本身的模塊,經過關鍵字exports露出接口方法,模塊名爲該文件名server,以後」server模塊「能夠被其餘模塊引用。

建立index.js文件,寫入代碼並引用server模塊:

1 var server = require("./server");
2 
3 server.serverStart();

 

對請求進行路由選擇

爲路由提供請求的url和其餘GET和POST參數,路由根據這些數據來分發請求到具體的處理程序。所以,咱們須要查看並解析http請求,提取出url和get/post參數,暫且將這一解析功能歸於http服務器一部分。請求數據都包含在request對象中,引入內置模塊url和querystring來解析它。

 1 var http = require('http');
 2 var url = require('url');
 3 
 4 function serverStart(){
 5         http.createServer(function(req,res){
 6             var pathname = url.parse(req.url).pathname;
 7             console.log('http request for '+pathname+' recieved');
 8             
 9           res.writeHead(200,{'Content-Type':'text/html'});
10           res.write('<h1>Node.js</h1>');  
11           res.end("");
12         }).listen(3000,'127.0.0.1');
13         
14         console.log('http server start on port 3000');
15 }
16 
17 exports.serverStart = serverStart;

經過第6行的解析,不一樣的請求路徑在第7行輸出不一樣的pathname。

創建一個名爲router.js的文件,編寫路由代碼以下:

1 function route(pathname){
2   console.log('about to route a request for'+pathname);
3 }
4 
5 exports.route = route;

服務器應該知道路由的存在,並加以組合有效地利用。這裏能夠採用硬編碼方式或依賴注入的方式實現組合,後者更好。

改造server.js以下:

 1 var http = require('http');
 2 var url = require('url');
 3 
 4 function serverStart(route){
 5         http.createServer(function(req,res){
 6             var pathname = url.parse(req.url).pathname;
 7             console.log('http request for '+pathname+' recieved');
 8             
 9  route(pathname);
10             
11           res.writeHead(200,{'Content-Type':'text/html'});
12           res.write('<h1>Node.js</h1>');  
13           res.end("");
14         }).listen(3000,'127.0.0.1');
15         
16         console.log('http server start on port 3000');
17 }
18 
19 exports.serverStart = serverStart;

改造index.js以下:

1 var server = require("./server");
2 var router = require("./router");
3 
4 server.serverStart(router.route);

編譯運行,可看到路由模塊被server模塊引入。

行爲驅動執行

在index中,咱們將router對象傳遞進去,服務器隨後調用這個對象的route函數。但其實服務器自身並不須要這個對象,它只是須要執行某個動做,而這個動做由該對象來完成。具體的細節服務器並不關心,由於這是執行這個動做的那個對象的事。這就是函數式編程。但歸根結底,到了最後一層總有一個對象是真正關心細節並完成細節,輸出結果的。像現實世界的上級把任務給下級,下級給再下級,最終總要有人完成這個任務纔好!

路由處處理程序

路由用來轉發請求,將請求分發給實際的處理程序,讓他們解決請求並回復,所以新建請求處理模塊requestHandlers.js,代碼以下:

 1 function start(){
 2   console.log("request handler 'start' was called");
 3 }
 4 
 5 function upload(){
 6   console.log("request handler 'upload' was called");
 7 }
 8 
 9 exports.start = start;
10 exports.upload = upload;

有了這個模塊,路由就有路可尋了。

如今須要將處理程序經過一個對象來傳遞並將對象注入到route()函數中,而javascript的對象是」key:value「組合,知足咱們的須要。將存放處理程序的對象引入index,以下:

 1 var server = require("./server");
 2 var router = require("./router");
 3 var requestHandlers = require("./requestHandlers");
 4 
 5 var handler = {};
 6 handler['/'] = requestHandlers.start;
 7 handler['/start'] = requestHandlers.start;
 8 handler['/upload'] = requestHandlers.upload;
 9 
10 server.serverStart(router.route,handler);

路徑爲」/「或"/start"的請求由start處理,"/upload"由upload處理。

將該對象做爲參數傳給服務器,server.js模塊代碼改成:

 1 var http = require('http');
 2 var url = require('url');
 3 
 4 function serverStart(route,handle){
 5         http.createServer(function(req,res){
 6             var pathname = url.parse(req.url).pathname;
 7             console.log('http request for ''+pathname+'' recieved');
 8             
 9             route(handle,pathname);
10             
11           res.writeHead(200,{'Content-Type':'text/html'});
12           res.write('<h1>Node.js</h1>');  
13           res.end("");
14         }).listen(3000,'127.0.0.1');
15         
16         console.log('http server start on port 3000');
17 }
18 
19 exports.serverStart = serverStart;

handle對象傳遞給了route,此時還得修改router模塊,以下:

 1 function route(handle,pathname){
 2   console.log('about to route a request for'+pathname);
 3   
 4     if(typeof handle[pathname] === 'function'){
 5       handle[pathname]();
 6     }
 7     else{
 8       console.log('no request handler found for'+pathname);
 9     }
10 }
11 
12 exports.route = route;

首先檢查對應的處理程序是否存在,存在則直接調用相應的函數,不然提示沒有找到對應的函數。這時,服務器,路由,處理程序就聯繫在一塊兒了。

讓請求處理程序做出響應

web程序通常是基於http的請求-應答模式,這樣服務器和瀏覽器能夠實現通話。

一種很差的實現方式

這種方式讓服務器的函數直接返回展現給用戶的信息。這就須要同時修改服務器、路由、請求處理函數的部分代碼:

requestHandlers.js

 1 function start(){
 2   console.log("request handler 'start' was called");
 3   return "hello start!";
 4 }
 5 
 6 function upload(){
 7   console.log("request handler 'upload' was called");
 8   return "hello upload!";
 9 }
10 
11 exports.start = start;
12 exports.upload = upload;

requestHandlers.js

 1 function route(handle,pathname){
 2   console.log('about to route a request for'+pathname);
 3   
 4     if(typeof handle[pathname] === 'function'){
 5       return handle[pathname]();
 6     }
 7     else{
 8       console.log('no request handler found for'+pathname);
 9       return "404 not found!"
10     }
11 }
12 
13 exports.route = route;

server.js

 1 var http = require('http');
 2 var url = require('url');
 3 
 4 function serverStart(route,handle){
 5         http.createServer(function(req,res){
 6             var pathname = url.parse(req.url).pathname;
 7             console.log('http request for '+pathname+' recieved');
 8                         
 9             
10           res.writeHead(200,{'Content-Type':'text/html'});
11           var content = route(handle,pathname);
12           res.write(content);  
13           res.end("");
14         }).listen(3000,'127.0.0.1');
15         
16         console.log('http server start on port 3000');
17 }
18 
19 exports.serverStart = serverStart;

這樣的模式運行的也很好,但當有請求處理程序須要執行非阻塞操做,就「掛」了。

阻塞與非阻塞

修改requestHandlers.js

 1 function start(){
 2   console.log("request handler 'start' was called");
 3   
 4   function sleep(milliSeconds){
 5       var startTime = new Date().getTime();
 6       while(new Date().getTime() < startTime + milliSeconds)
 7         ;  
 8   }
 9   
10   sleep(10000);
11   return "hello start!";
12 }
13 
14 function upload(){
15   console.log("request handler 'upload' was called");
16   return "hello upload!";
17 }
18 
19 exports.start = start;
20 exports.upload = upload;

上述的sleep函數會讓方法停頓10秒,而後纔會返回結果。實際場景中,須要計算或是查找數據庫等阻塞操做很是多。

如今經過start和upload便可模擬阻塞,打開兩個空白頁面分別輸入「localhost:3000/start」和"localhost:3000/upload",先進行start請求在快速切換到量以頁面進行upload請求,發現start頁面請求恢復用了10秒,而upload也用了10秒。緣由是,start中的sleep阻塞了全部其餘處理。

而nodejs的特性之一是——單線程,能夠在不增長線程狀況下,對任務進行並行處理。這裏的機制是它使用輪詢(event loop)來實現並行操做,全部儘可能避免阻塞操做而是用非阻塞操做。要使用非阻塞,須要使用回調,將函數做爲參數傳遞給其餘須要費時間處理的函數。

對於nodejs能夠形象比喻成「嘿,probablyExpensiveFunction()(須要費時間處理的函數),繼續作你的事情,我(nodejs線程)先不等你了,繼續去處理後面的代碼,請你提供一個回調callBackFunction(),等你處理完了我會去調用該回調函數的。」

一種錯誤的非阻塞方式

requestHandlers.js

 1 var exec = require("child_process").exec;
 2 
 3 function start(){
 4   console.log("request handler 'start' was called");
 5   var content = "empty";
 6   
 7   exec("ls-lah",function(error,stdout,stderr){
 8       content = stdout;
 9   });    
10   return content;
11 }
12 
13 function upload(){
14   console.log("request handler 'upload' was called");
15   return "hello upload!";
16 }
17 
18 exports.start = start;
19 exports.upload = upload;

引入另外一個模塊「child_process」,實現非阻塞操做exec()。

exec()的做用是從nodejs執行一個shell命令,例子中使用它獲取當前目錄下全部文件("ls-lah"),而後當start請求時將文件信息輸出到瀏覽器。執行該例子,瀏覽器輸出「empty」。緣由在於,nodejs是同步執行,而爲了非阻塞,exec()使用了回調函數,回調函數做爲參數傳遞給exec(),而exec()是異步操做,即當他還沒來得及執行回調,nodejs的同步性導致nodejs執行了exec()的下一個語句,這時候content並無等來exec()的賦值,所以他仍是「empty」。這和javascript中的同步異步很相像,每當須要執行ajax,而且須要ajax的結果時,咱們將下一步的代碼放在ajax執行成功時的回調裏。

非阻塞方式對請求進行響應

咱們能夠採用值傳遞的方式將請求處理函數返回的內容再回傳給http服務器。上述方式都是將請求處理函數的結果返回給http服務器,而另外一種方式是將服務器傳遞給內容,即將response對象傳遞給請求處理程序,程序隨後採用該對象上的方法對對請求進行響應。

server.js

 1 var http = require('http');
 2 var url = require('url');
 3 
 4 function serverStart(route,handle){
 5         http.createServer(function(req,res){
 6             var pathname = url.parse(req.url).pathname;
 7             console.log('http request for '+pathname+' recieved');
 8                                               
 9           route(handle,pathname,res);
10          
11         }).listen(3000,'127.0.0.1');
12         
13         console.log('http server start on port 3000');
14 }
15 
16 exports.serverStart = serverStart;

以上將server.js中有關response的函數調用所有移除,將response對象傳給route。

router.js

 1 function route(handle,pathname,response){
 2   console.log('about to route a request for'+pathname);
 3   
 4     if(typeof handle[pathname] === 'function'){
 5       return handle[pathname](response);
 6     }
 7     else{
 8       console.log('no request handler found for'+pathname);
 9       response.writeHead(404,{"Content-Type":"text/plain"});
10       response.write("404 not found!");
11       response.end();
12     }
13 }
14 
15 exports.route = route;

requestHandlers.js

 1 var exec = require("child_process").exec;
 2 
 3 function start(response){
 4   console.log("request handler 'start' was called");  
 5   
 6   exec("ls-lah",function(error,stdout,stderr){
 7        response.writeHead(200,{"Content-Type":"text/plain"});
 8        response.write(stdout);
 9        response.end();
10   });    
11   
12 }
13 
14 function upload(response){
15   console.log("request handler 'upload' was called");
16   
17   response.writeHead(200,{"Content-Type":"text/plain"});
18     response.write("hello upload");
19     response.end();
20 }
21 
22 exports.start = start;
23 exports.upload = upload;

處理程序接收response對象,由該對象作出直接響應。在此編譯,打開瀏覽器請求,「localhost:3000/start」不會對"localhost:3000/upload"形成阻塞。

更有用的場景

 

處理post請求

 1 var exec = require("child_process").exec;
 2 
 3 function start(response){
 4   console.log("request handler 'start' was called");  
 5   
 6   var body = '<html>'+'<head>'+'<body>'+'<form action="/upload" method="post">'+'<textarea name="text" rows="20" cols="50"></textarea>'+
 7         '<input type="submit" value="submit"/>'+'</form></body></html>';
 8   
 9   response.writeHead(200,{"Content-Type":"text/html"});
10     response.write(body);
11     response.end();         
12   
13 }
14 
15 function upload(response){
16   console.log("request handler 'upload' was called");
17   
18   response.writeHead(200,{"Content-Type":"text/plain"});
19     response.write("hello upload");
20     response.end();
21 }
22 
23 exports.start = start;
24 exports.upload = upload;

以上是修改後的requestHandlers.js,運行後是個簡單的表單,包括文本域和提交按鈕。post數據有時候會很大,爲使過程不阻塞,nodejs會將post數據拆分紅不少很小的數據塊,而後經過觸發特定的事件,將這些小數據塊傳給回調函數。特定事件有data(表示新的小數據塊到了)和end(表示全部數據塊都已經接收完畢)。這時候咱們須要告訴nodejs事件觸發的時候調用哪些回調函數,經過什麼方式告訴呢?在request對象上註冊監聽器(listener)來實現。獲取全部來自請求端的數據並將數據傳給應用層處理,是http服務器的事。因此直接在server裏處理post數據,而後將最終的數據傳給路由和請求處理器,讓他們來進一步處理。

server.js

 1 function serverStart(route,handle){
 2         http.createServer(function(req,res){
 3             var postData = "";
 4             var pathname = url.parse(req.url).pathname;
 5             console.log('http request for '+pathname+' recieved');
 6             
 7             request.setEncoding("utf8");
 8             
 9             request.addListener("data",function(postDataChunk){
10               postData += postDataChunk;
11               console.log('received post data chunk'+postDataChunk+".");
12             });
13             
14             request.addListener("end",function(postDataChunk){
15               route(handle,pathname,res,postData);
16             });                                      
17                    
18         }).listen(3000,'127.0.0.1');
19         
20         console.log('http server start on port 3000');
21 }
22 
23 exports.serverStart = serverStart;

上述代碼作了3件事,首先設置接收數據格式爲utf8,而後綁定data事件的監聽器,用於接收每次傳遞的新數據塊,複製給postData,最後將路由調用移到end中,確保數據接收完成在觸發而且只觸發一次。

requestHandlers.js

 1 var querystring = require("querystring");
 2 function start(response,postData){
 3   console.log("request handler 'start' was called");  
 4   
 5   var body = '<html>'+'<head>'+'<body>'+'<form action="/upload" method="post">'+'<textarea name="text" rows="20" cols="50"></textarea>'+
 6         '<input type="submit" value="submit"/>'+'</form></body></html>';
 7   
 8   response.writeHead(200,{"Content-Type":"text/html"});
 9     response.write(body);
10     response.end();         
11   
12 }
13 
14 function upload(response,postData){
15   console.log("request handler 'upload' was called");
16   
17   response.writeHead(200,{"Content-Type":"text/plain"});
18     response.write("you are sent:"+querystring.parse(postData).text);
19     response.end();
20 }
21 
22 exports.start = start;
23 exports.upload = upload;

經過querystring篩選出咱們感興趣的部分,以上就是關於post數據處理的內容。

處理文件上傳

處理文件上傳就是處理post數據,有時候麻煩的是細節處理。因此這裏使用現成的方案,即外部模塊node-formidable,用nmp命令安裝:

nmp install formidable

該模塊作的就是經過http post請求提交的表單,在nodejs中能夠被解析。咱們建立一個IncomingForm,它是對提交的表單的抽象表示,以後就能夠用他來解析request對象,獲取須要的字段數據。關於該模塊具體信息及工做原理,其官網有實例。

如今的問題是保存在本地硬盤的內容如何顯示在瀏覽器中。咱們須要將其讀取到服務器,使用fs模塊完成。

 1 var querystring = require("querystring"),
 2     fs = require("fs");
 3 function start(response,postData){
 4   console.log("request handler 'start' was called");  
 5   
 6   var body = '<html>'+'<head>'+'<body>'+'<form action="/upload" method="post">'+'<textarea name="text" rows="20" cols="50"></textarea>'+
 7         '<input type="submit" value="submit"/>'+'</form></body></html>';
 8   
 9   response.writeHead(200,{"Content-Type":"text/html"});
10     response.write(body);
11     response.end();         
12   
13 }
14 
15 function upload(response,postData){
16   console.log("request handler 'upload' was called");
17   
18   response.writeHead(200,{"Content-Type":"text/plain"});
19     response.write("you are sent:"+querystring.parse(postData).text);
20     response.end();
21 }
22 
23 function show(response,postData){
24   console.log("request handler 'show' was called");
25   fs.readFile("/tmp/test.png","binary",function(error,file){
26       if(error){
27            response.writeHead(500,{"Content-Type":"text/plain"});
28            response.write(error+"\n");
29              response.end();
30       }else{
31           response.writeHead(200,{"Content-Type":"image/png"});
32           response.write(file,"binary");
33           response.end();
34       }
35   });
36 }
37 
38 exports.start = start;
39 exports.upload = upload;
40 exports.show = show;

將其添加到index.js的路由映射表中:

 1 var server = require("./server");
 2 var router = require("./router");
 3 var requestHandlers = require("./requestHandlers");
 4 
 5 var handler = {};
 6 handler['/'] = requestHandlers.start;
 7 handler['/start'] = requestHandlers.start;
 8 handler['/upload'] = requestHandlers.upload;
 9 handler['/show'] = requestHandlers.show;
10 
11 server.serverStart(router.route,handler);

編譯,瀏覽器請求便可看見圖片。

最後,

  須要在/start表單添加文件上傳元素

  將formidable整合到upload中用於將上傳的圖片保存到指定路徑

  將上傳的圖片內嵌到/upload url輸出的html中

第一項,移除此前的文本區,添加上傳組件和「multipart/form-data」編碼類型:

 1 var querystring = require("querystring"),
 2     fs = require("fs");
 3 function start(response,postData){
 4   console.log("request handler 'start' was called");  
 5   
 6   var body = '<html>'+'<head>'+'<body>'+'<form action="/upload" method="post" enctype="multipart/form-data">'+
 7         '<input type="file" name="upload"/><input type="submit" value="upload file">'+'</form></body></html>';
 8   
 9   response.writeHead(200,{"Content-Type":"text/html"});
10     response.write(body);
11     response.end();         
12   
13 }
14 
15 function upload(response,postData){
16   console.log("request handler 'upload' was called");
17   
18   response.writeHead(200,{"Content-Type":"text/plain"});
19     response.write("you are sent:"+querystring.parse(postData).text);
20     response.end();
21 }
22 
23 function show(response,postData){
24   console.log("request handler 'show' was called");
25   fs.readFile("/tmp/test.png","binary",function(error,file){
26       if(error){
27            response.writeHead(500,{"Content-Type":"text/plain"});
28            response.write(error+"\n");
29              response.end();
30       }else{
31           response.writeHead(200,{"Content-Type":"image/png"});
32           response.write(file,"binary");
33           response.end();
34       }
35   });
36 }
37 
38 exports.start = start;
39 exports.upload = upload;
40 exports.show = show;
View Code

這時候咱們須要在upload中對請求中的上傳文件進行處理,這樣的話須要將request傳給node-formidable的form-parse函數。因爲nodejs不會對數據做緩衝,因而必須將request對象傳入。

server.js:

 1 var http = require('http');
 2 var url = require('url');
 3 
 4 function serverStart(route,handle){
 5         http.createServer(function(req,res){
 6             
 7             var pathname = url.parse(req.url).pathname;
 8             console.log('http request for '+pathname+' recieved');
 9             
10           route(route,handle,res,req);
11                    
12         }).listen(3000,'127.0.0.1');
13         
14         console.log('http server start on port 3000');
15 }
16 
17 exports.serverStart = serverStart;
View Code

router.js:

 1 function route(handle,pathname,response,request){
 2   console.log('about to route a request for'+pathname);
 3   
 4     if(typeof handle[pathname] === 'function'){
 5       return handle[pathname](response,request);
 6     }
 7     else{
 8       console.log('no request handler found for'+pathname);
 9       response.writeHead(404,{"Content-Type":"text/plain"});
10       response.write("404 not found!");
11       response.end();
12     }
13 }
14 
15 exports.route = route;
View Code

將上傳和重命名的操做放在一塊兒:

 1 var querystring = require("querystring"),
 2     fs = require("fs"),
 3     formidable = require("formidable");
 4 function start(response){
 5   console.log("request handler 'start' was called");  
 6   
 7   var body = '<html>'+'<head>'+'<body>'+'<form action="/upload" method="post" enctype="multipart/form-data">'+
 8         '<input type="file" name="upload"/><input type="submit" value="upload file">'+'</form></body></html>';
 9   
10   response.writeHead(200,{"Content-Type":"text/html"});
11     response.write(body);
12     response.end();         
13   
14 }
15 
16 function upload(response,postData){
17   console.log("request handler 'upload' was called");
18   
19   var form = new formidable.IncomingForm();
20   form.parse(request,function(error,fields,files){
21     fs.renameSync(file.upload.path,"/tmp/test.png");
22     response.writeHead(200,{"Content-Type":"text/html"});
23       response.write("image:</br>");
24       response.write("<image src='/show'/>");
25       response.end();
26   });
27   
28 }
29 
30 function show(response,postData){
31   console.log("request handler 'show' was called");
32   fs.readFile("/tmp/test.png","binary",function(error,file){
33       if(error){
34            response.writeHead(500,{"Content-Type":"text/plain"});
35            response.write(error+"\n");
36              response.end();
37       }else{
38           response.writeHead(200,{"Content-Type":"image/png"});
39           response.write(file,"binary");
40           response.end();
41       }
42   });
43 }
44 
45 exports.start = start;
46 exports.upload = upload;
47 exports.show = show;
View Code

好,編譯,重啓瀏覽器。選擇本地圖片,上傳並顯示!

 

結構圖以下:

 

該實例有關的技術點:

  服務器端javascript

  函數式編程

  阻塞與非阻塞

  單線程

  回調

  事件

  nodejs模塊

沒有介紹到的還有數據庫操做、單元測試、開發外部模塊等。

 

注:本文摘抄自《nodejs入門》

相關文章
相關標籤/搜索