《Node入門》讀書筆記——用Node.js開發一個小應用

如需轉載請註明出處 http://blog.csdn.net/as645788

Android APP的開發告一段落,一個穩定的、實現了基本功能的APP已經交付用戶使用了!我和老闆交流了下,接下來準備轉戰Node.js了,而且一部分前端的功能也要做進去!哈哈哈~~~接下來要朝一個全(zuo)棧(si)工程師進發了,想想都有點小激動呢!這幾天一直在學新東西,HTML CSS JavaScript jQuery SQL bootstrap Node.js ···········


(好吧,看着這麼多前端+後端的知識,我已經凌亂在風中了-。-·····)
要學的東西是蠻多的,時間又緊,後來我在知乎一番搜索後決定主攻Node.js,SQL等用到時再去查語句怎麼寫,JavaScript學習基本的語法和關鍵的一些思想(回調、閉包····)HTML CSS 力求能看懂,jQuery也嘗試着寫,這裏先給大家推薦一個不錯的網站,世界上最大學習網站開發的地方 w3schools (簡單直白的英文網站,可以鍛鍊下自己看英文文檔的能力,貌似需要翻牆,請自備梯子),沒有梯子的童鞋可以去 菜鳥教程(中國人自己翻譯的網站,內容基本一致),小小的吐槽下:w3schools 的exercise太水了,幾道題基本做法都差不多,最多就修改兩三個地方,並且學完之後的測試居然就是24道選擇題(很多還都是判斷題······),還以爲很難呢·······不過貌似人家盈利的方式就是最後學完後的certificate,價格是驚人的 95 美刀(在國外知識確實很值錢-。-)


廢話不多說,進入正題吧,我是在網上看的這本《Node入門》總共就42頁,一邊看一邊背書裏的代碼自己寫,一天就看完了,還是有不少收穫的。
首先咱們要安裝Node.js,我用的是Windows版本的安裝包,直接下載安裝就好,然後大家還需要裝一下sublime text 3。下面是我們最終要達到的三個目標。

  • 用戶可以通過瀏覽器使用我們的應用。
  • 當用戶請求http://127.0.0.1:8888/start時,可以看到一個歡迎頁面,頁面上有一個文件上傳的表單。
  • 用戶可以選擇一個圖片並提交表單,隨後文件將被上傳到http://127.0.0.1:8888/upload,該頁面完成上傳後會把圖片顯示在頁面上。

最後的效果是這樣:上傳圖片的效果

圖片上傳後的效果

書裏有一部分關於直接把函數作爲參數進行傳值的話我覺得講的很好,現在摘錄出來方便大家理解下什麼是 函數式編程
行爲驅動執行

請允許我再次脫離主題,在這裏談一談函數式編程。

將函數作爲參數傳遞並不僅僅出於技術上的考量。對軟件設計來說,這其實是個哲學問題。想想這樣的場景:在index文件中,我們可以將router對象傳遞進去,服務器隨後可以調用這個對象的route函數。

就像這樣,我們傳遞一個東西,然後服務器利用這個東西來完成一些事。嗨那個叫路由的東西,能幫我把這個路由一下嗎?

但是服務器其實不需要這樣的東西。它只需要把事情做完就行,其實爲了把事情做完,你根本不需要東西,你需要的是動作。也就是說,你不需要名詞,你需要動詞

理解了這個概念裏最核心、最基本的思想轉換後,我自然而然地理解了函數編程。

關於Node.js還有一點需要理解的是阻塞IO與非阻塞IO(使用回調函數)的不同

簡單來說,假設我們要去快餐店喫飯,有兩種不同服務模式的快餐店,一種是基於事件驅動的(我們的node服務器),一種不是(像iis,apache)。對於傳統的服務器,在接收到你的請求之後,直到他完成你的請求,否則他不會去接待下一個用戶。當服務員輸入你的訂單之後還有很多事情要做,處理你的支付,幫你倒水,還有一段時間(不確定時長)去等待廚房準備好你的漢堡。服務員(相當於服務器上的線程)每次只能接待一位顧客,直到完成當前顧客的接待之後,纔會去接待下一位顧客。很顯然,這種方式效率不高,他浪費了太多的時間在等待廚房做漢堡的工作上。而現實中的快餐店採用的是另外一種模式,當接收到你的訂單之後,他會給你一個號碼牌,這個號碼牌就相當於回調函數。接着他會去接待下一位顧客。當你的訂餐準備好之後,服務員會呼叫你的號碼叫你來取餐。這就是node採用的模式,看得出他要高效的多。

一路看着書往下寫,我們把這個小Node.js程序分成了三個主要的模塊:server(服務器)、router(分發處理不同的請求)、requestHandle(真正處理請求的部分),剩下的就是細節的完善。


最後貼上全部的源碼來供大家學習吧,windows下有一個坑,那個坑花了我兩個小時才找到,具體見代碼·····

主頁文件index.js

var server = require('./server'); //本地寫的server.js 文件
var router = require('./router');
var requestHandle = require('./requestHandle');

var handle = {};
handle["/"] = requestHandle.start;
handle['/start'] = requestHandle.start;
handle['/upload'] = requestHandle.upload;
handle['/directory'] = requestHandle.directory;
handle['/display'] = requestHandle.display;


server.start(router.route,handle);

server.js

//調用node.js的自帶模塊http,並將其返回值賦值給 var http
var http = require("http");
var url = require("url");

function start(route, handle) {
//用onRequest作爲回調函數
    function onRequest(request, response) {

        var pathName = url.parse(request.url).pathname;
        var postDate = "";

        console.log("Request for " + pathName + " has received.");
        route(pathName,handle,response,request);        
    };

    http.createServer(onRequest).listen(8888);
    console.log("server has start");
}

exports.start = start;

router.js

function route(pathName,handle,response,request){

  console.log("start router with handle and path name is " + pathName);

  if (typeof handle[pathName] === "function") {
      return handle[pathName](response,request); //Node.js中這個執行的方法太酷了
  }else{
      console.log("404 not find ");
      response.writeHead(404,{"Content-Type":"text/plain"});
      response.write("404 not found");
      response.end();
  }
}

exports.route = route;

requestHandle.js

var exec = require('child_process').exec;
var queryString = require('querystring');
var fs = require('fs');
var formidable = require('formidable');

function start(response) {
    console.log("handler 'start' was called");

    //僅僅是爲了學習才這麼把html寫這裏的,實際中是絕不能這麼醜陋地把html直接寫在處理程序裏的
    var body = '<html>' +
        '<head>' +
        '<meta http-equiv="content-type" content="text/html;charset=UTF-8">' +
        '</head>' +
        '<body>' +
        //定義submit後的跳轉的url是/upload,且是一個post請求
        '<form action="/upload" method="post" enctype = "multipart/form-data">' +
        '<input type="file" name="upload" multiple="multiple" ><br/>' +
        '<input type="submit" value="upload file">' +
        '</form>' +
        '</body>' +
        '</html>';
    response.writeHead(200, {
        "Content-Type": "text/html"
    });
    response.write(body);
    response.end();
}


function upload(response, request) {
    console.log("handler 'upload' was called");
    var form = new formidable.IncomingForm();

    //關鍵:這裏需要改變form的默認的上傳路徑,且在windows下由於權限的問題,需要手動創建tmp文件夾
    form.uploadDir = 'tmp';
    form.parse(request, function(error, fields, files) {
          console.log(files.upload.path);
          // fs.renameSync(files.upload.path, "/tmp/test.png");
          //考慮到這個是Sync同步操作,則必須對異常進行捕獲,Node.js不推薦同步操作
          try{
            fs.renameSync(files.upload.path,'tmp/test.png');
          }catch(e){
            console.log(e);
          }         
        response.writeHead(200, {
            "Content-Type": "text/html"
        });
        response.write('<img src="/display"/>');
        response.end();  

    });
}

function display(response) {
    console.log("handler 'display' was called");
    fs.readFile("tmp/test.png",function(error, file) {
        if (error) {
            console.log("sorry");
        } else {
           response.writeHead(200,{"Content-Type":"image/png"});
           // response.write(data);
           response.write(file);
           response.end();
        };
    });
}

/*//響應用戶輸入的display方法
function display(response,postDate) {
    console.log("handler 'display' was called");
    response.writeHead(200, {
        "Content-Type": "text/plain"
    });
    response.write("You input " + queryString.parse(postDate).text);
    response.end();
}*/

exports.start = start;
exports.upload = upload;
exports.directory = directory;
exports.display = display;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章