2012-02-25 47 views
16

我在寫一個簡單的請求處理程序來返回一對css文件。使用fs.readFileSync這很容易。但是,我很難用異步版本的readFile完成相同的任務。以下是我的代碼。讓我的response.write()方法調用拆分兩個不同的回調似乎是有問題的。有人能指出我做錯了什麼嗎?有趣的是,如果我在第一個else語句中放入response.end(),這段代碼就可以工作。但是,這會產生一個問題,即第二個css文件不會被返回(因爲response.end()已經被觸發)。使用fs.readFile在Node.js中讀取和返回多個文件

function css(response) { 

    response.writeHead(200, {"Content-Type": "text/css"}); 

    fs.readFile('css/bootstrap.css', function(error, content){ 
    if(error){ 
     console.log(error); 
    } 
    else{ 
     response.write(content); 
    } 
    }); 
    fs.readFile('css/bootstrap-responsive.css', function(error, content){ 
    if(error){ 
     console.log(error); 
    } 
    else{ 
     response.write(content) 
    } 
    }); 
    response.end(); 
} 
+0

所以,你是你試圖追加兩個CSS文件在一個響應? – loganfsmyth 2012-02-26 00:15:12

+0

是的,沒錯。 – hughesdan 2012-02-26 00:42:11

+0

每當有人提出請求時,您是否有理由讀取文件?你期望他們經常改變嗎?如果沒有,只需在服務器啓動時將它們讀入內存並在需要時返回。工作更快。 – Evgeny 2013-02-27 00:24:31

回答

26

您所擁有的主要問題是response.end()被立即調用。您需要在文件完成response.write調用後才調用它。

最簡單的方法是使用控制流庫。管理多個異步回調通常很複雜。

https://github.com/joyent/node/wiki/modules#wiki-async-flow

我將使用async庫,因爲它是一個我最瞭解。

var fs = require('fs'); 
var async = require('async'); 

function css(response) { 
    response.writeHead(200, {"Content-Type": "text/css"}); 

    async.eachSeries(
    // Pass items to iterate over 
    ['css/bootstrap.css', 'css/bootstrap-responsive.css'], 
    // Pass iterator function that is called for each item 
    function(filename, cb) { 
     fs.readFile(filename, function(err, content) { 
     if (!err) { 
      response.write(content); 
     } 

     // Calling cb makes it go to the next item. 
     cb(err); 
     }); 
    }, 
    // Final callback after each item has been iterated over. 
    function(err) { 
     response.end() 
    } 
); 
} 

如果你想做到這一點沒有圖書館,或者只是想另一種方式,這是我怎麼會更直接地做到這一點。基本上你保留一個count並且一旦兩個文件讀取都完成,就調用end

function css(response) { 
    response.writeHead(200, {"Content-Type": "text/css"}); 

    var count = 0; 
    var handler = function(error, content){ 
    count++; 
    if (error){ 
     console.log(error); 
    } 
    else{ 
     response.write(content); 
    } 

    if (count == 2) { 
     response.end(); 
    } 
    } 

    fs.readFile('css/bootstrap.css', handler); 
    fs.readFile('css/bootstrap-responsive.css', handler); 
} 
+1

謝謝。這是一個非常豐富的答案! – hughesdan 2012-02-26 03:18:37

+0

任何特定的地方,你會稱之爲函數CSS? – Winnemucca 2017-08-01 17:36:51

+0

從回調內部到https://nodejs.org/api/http.html#http_http_createserver_requestlistener或任何Node的API服務器軟件包。 – loganfsmyth 2017-08-01 18:02:58

1

有一個簡單的常見解決方案,用一個回調來獲得它們。 您可以將它放置在項目的任何位置以便在許多不同的情況下重用。

var FS = require('fs'); 

/** 
* Abstract helper to asyncly read a bulk of files 
* Note that `cb` will receive an array of errors for each file as an array of files data 
* Keys in resulting arrays will be the same as in `paths` 
* 
* @param {Array} paths - file paths array 
* @param {Function} cb 
* @param {Array} errors - a list of file reading error 
* @param {Array} data - a list of file content data 
*/ 
function FS_readFiles (paths, cb) { 
    var result = [], errors = [], l = paths.length; 
    paths.forEach(function (path, k) { 

     FS.readFile(path, function (err, data) { 
      // decrease waiting files 
      --l; 
      // just skip non-npm packages and decrease valid files count 
      err && (errors[k] = err); 
      !err && (result[k] = data); 
      // invoke cb if all read 
      !l && cb (errors.length? errors : undef, result); 
     }); 

    }); 
} 

只要把裏面的批量文件,它會回報給你他們每個人作爲緩衝。 簡單的例子:

var cssFiles = [ 
    'css/bootstrap.css', 
    'css/bootstrap-responsive.css' 
]; 

function css(response) { 
    FS_readFiles(cssFiles, function (errors, data) { 
     response.writeHead(200, {"Content-Type": "text/css"}); 
     data.forEach(function (v) { 
      response.write(v); 
     }); 
     response.end(); 
    }); 
} 

Offtopic:順便說一下,像這樣的請求,你最好是緩存像nginx的或清漆前端代理服務器上。這永遠不會改變。

-1

異步是一個很棒的lib。然而,這些事情的標準正朝着承諾處理多個異步操作的方向發展。實際上在ECMAScript6中this will be a standard part of the library。有幾個實現promise的庫包括JQuery。然而,對於節點,我喜歡用'q'

下面是使用承諾相同的代碼:一注..你可能想移動第一writeHead調用將第一次成功讀取一致。

var Q = require('q'); 

function css(response) { 
    response.writeHead(200, {"Content-Type": "text/css"}); 
    var defer = Q.defer(); 
    fs.readFile('css/bootstrap.css', function(error, content){ 
     if(error){ 

      defer.reject(error) 
     } 
     else{ 
     response.write(content); 
     defer.resolve(); 
     } 
    }); 
    defer.promise.then(function() { //this gets executed when the first read succeeds and is written 
     var secondDefer = Q.defer(); 
     fs.readFile('css/bootstrap-responsive.css', function(error, content){ 
      if(error){ 
       secondDefer.reject(error); 
      } 
      else{ 
       response.write(content); 
       secondDefer.resolve(); 
      } 
     }); 
     return secondDefer.promise; 
    }, 
    function(error) { //this gets called when the first read fails 
     console.log(error); 
     //other error handling 
    }). 
    done(function() { 
     response.end(); 
    }, 
    function(error) { //this is the error handler for the second read fails 
     console.log(error); 
     response.end(); //gotta call end anyway 
    }); 

} 
0

您可以簡單地依靠html5承諾。代碼可以簡單如下:

var promises= ['file1.css', 'file2.css'].map(function(_path){ 
    return new Promise(function(_path, resolve, reject){ 
     fs.readFile(_path, 'utf8', function(err, data){ 
      if(err){ 
       console.log(err); 
       resolve(""); //following the same code flow 
      }else{ 
       resolve(data); 
      } 
     }); 
    }.bind(this, _path)); 
}); 

Promise.all(promises).then(function(results){ 
    //Put your callback logic here 
    response.writeHead(200, {"Content-Type": "text/css"}); 
    results.forEach(function(content){response.write(content)}); 
    response.end(); 
}); 
相關問題