Node.js開發入門—HTTP檔案伺服器

來源:互聯網
上載者:User

Node.js開發入門—HTTP檔案伺服器

HelloWorld樣本只有示範意義,這次我們來搞一個實際的例子:檔案伺服器。我們使用Node.js建立一個HTTP協議的檔案伺服器,你可以使用瀏覽器或其它下載工具到檔案伺服器上下載檔案。

用Node.js實現的HTTP檔案伺服器,比我在Qt網路編程實戰之HTTP伺服器視頻課程裡用Qt實現的版本,複雜度要小太多太多了。

為了讀取檔案,我們會用到File System模組(名字是”fs”),Stream,我們還要分析URL,區別HTTP方法,還會用到EventEmitter。

檔案伺服器FileServer的代碼

先上代碼吧,依然是簡單的:

// 引入http模組var http = require(http); var fs = require(fs);// 建立server,指定處理用戶端請求的函數http.createServer(    function(request, response) {        //判斷HTTP方法,只處理GET          if(request.method != GET){            response.writeHead(403);            response.end();            return null;        }        //此處也可使用URL模組來分析URL(https://nodejs.org/api/url.html)        var sep = request.url.indexOf('?');        var filePath = sep < 0 ? request.url : request.url.slice(0, sep);        console.log(GET file:  + filePath);        //當檔案存在時發送資料給用戶端,否則404        var fileStat = fs.stat(.+filePath,             function(err, stats){                if(err) {                    response.writeHead(404);                    response.end();                    return null;                }                //TODO:Content-Type應該根據檔案類型設定                response.writeHead(200, {Content-Type: text/plain, Content-Length: stats.size});                //使用Stream                var stream = fs.createReadStream(.+filePath);                stream.on('data',function(chunk){                    response.write(chunk);                });                stream.on('end',function(){                    response.end();                });                stream.on('error',function(){                    response.end();                });            }        );    }).listen(8000); console.log(Hello World start listen on port 8000);

最大的變化,就在傳遞給createServer方法的參數了。

我們根據request.method作了判斷,不是GET就返回403。如果是呢,就判斷檔案是否存在,不存在,返回404,存在就讀取資料寫給用戶端。邏輯就是這麼簡單。下面我們來介紹用到的新知識。

File System

要使用FileSystem,得用require引入fs模組,就如前面代碼裡那樣。File System的API老長老長了,看這裡吧:https://nodejs.org/api/fs.html。我們只說用到的特性。

擷取檔案狀態

在我們的FileServer裡,收到和用戶端請求時先通過fs.stat()方法擷取檔案狀態。fs.stat()方法原型如下:

fs.stat(path, callback)

第一個參數是檔案路徑,第二個參數是回呼函數。fs.stat()方法是非同步,結果通過回呼函數callback返回。callback的原型如下:

function(err, stats)

第一個參數指示是否出現了錯誤,第二個參數是一個對象,類型是fs.Stats,儲存了檔案的狀態資訊,比如大小、建立時間、修改時間等。

FileServer的代碼擷取到檔案狀態後,讀取大小,調用http.ServerResponse的writeHead方法,設定HTTP狀態代碼為200,還設定了Content-Length頭部。代碼如下:

response.writeHead(200, {Content-Type: text/plain, Content-Length: stats.size})
ReadStream

接下來呢,我們調用fs.createReadStream建立了一個ReadStream對象。ReadStream是Stream,也是EventEmitter。

fs.createReadStream方法原型如下:

fs.createReadStream(path[, options])

第一個參數是檔案路徑,第二個參數是可選的JSON對象,用來指定開啟檔案的一些選項,預設值如下:

{ flags: ‘r’,
encoding: null,
fd: null,
mode: 0666,
autoClose: true
}

autoClose屬性預設為true,讀完檔案或讀取出錯時,檔案會被自動關閉。fd屬性可以關聯一個已有的檔案描述符,這樣就會忽略path,根據一個已經開啟的檔案來建立流。options還可以有start和end項,指定起、止位置,讀取檔案的特定地區。如果我們要實現斷點續傳,就需要這個了,用法類似這樣:

fs.createReadStream('sample.mp4', {start: 1000, end: 10000});

encoding用來指定檔案的編碼,這對於文字檔有特殊的意義,目前支援’utf8’、’ascii’和’base64’。

ReadStream讀取資料是非同步,一塊一塊的讀,讀到一部分就發送一個data事件,資料呢,會傳遞給與事件關聯的listener(實際上是一個回調方法)。在我們的代碼裡,僅僅是調用response.write把資料寫給用戶端。注意,可能會多次調用response.write哦。又因為我們設定了Content-Length,所以不會採用chunked編碼方式。如果我們不設定Content-Length,那預設會啟用chunked方式。

ReadStream讀完檔案時會發射end事件,出錯時會發射error事件,我們監聽這兩個事件,簡單的終止響應。

我們在範例程式碼中看到了stream.on這種代碼,下面來解釋吧。

EventEmitter

Node.js基於V8引擎實現的事件驅動IO,是其最大最棒的特色之一。有了事件機制,就可以充分利用非同步IO突破單線程編程模型的效能瓶頸,使得用JavaScript作後端開發有了實際意義。

EventEmitter的基本用法

events.EventEmitter是一個簡單的事件發射器的實現,具有addListener、on、once、removeListener、emit等方法,開發人員可以很方便的調用這些API監聽某個事件或者發射某個事件。

我們在樣本中用到的fs.ReadStream就是一個EventEmitter,它實現了stream.Readable介面,而stream.Readable具有data、error、end、close、readable等事件。

通常我們使用EventEmitter的on或addListener來監聽一個事件,這個時間可能會多次觸發,每次觸發,我們提供的回調方法都會被調用。我們樣本中的代碼就是這樣:

        stream.on('data',function(chunk){            response.write(chunk);        });

Node.js的事件機制,會給某個事件關聯一個回調方法列表,這樣多個粉絲就可以監聽同一個事件。每個事件發射時,可能會帶有資料和狀態,這些資料是通過回調方法的參數傳遞出來的。那某一個特定的事件,它對應的回調方法的參數是什麼樣子的,則由事件定義的那個類(執行個體)來決定。EventEmitter的emit方法原型如下:

emitter.emit(event[, arg1][, arg2][, ...])

這個原型說明一個事件的回調方法可以有一個或多個參數,也可以沒有參數。要想知道某個事件的回調方法是否有參數、每個參數的含義,只好去找相關的API文檔。stream.Readable的data事件的參數是chunk,Buffer類型,代表讀到的資料。

如果我們只想監聽某個事件一次,則可以調用EventEmitter的once方法。要想移除一個事件監聽器,可以調用removeListener,想移除所有,則可以調用removeAllListener。

自訂事件

Node.js的很多模組都繼承自Event模組。我們自己也可以通過繼承EventEmitter來實現自己的對象,添加自己的自訂事件。

這裡有個簡單的例子:

var util=require(util);var events = require(events);function Ticker() {    var self = this;    events.EventEmitter.call(this);    setInterval(function(){        self.emit(tick)        },        1000    );}util.inherits(Ticker, events.EventEmitter);var ticker = new Ticker();ticker.on(tick, function() {  console.log(tick event);});

在這個簡單的例子裡,我們定義了Ticker對象,通過全域方法setInterval開啟了一個定時器,每隔1000毫秒發射一個名為“tick”的事件。

Node.js的工具模組封裝了繼承的方法,我們調用它來的inherits方法來完成Ticker對events.EventEmitter的繼承。

自訂事件的使用方法,和Node.js內建模組提供的事件的用法完全一樣。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.