上一節咱們實現了手動控制路由的示例,這節咱們來作一個完整的示例html
目錄結構
基於前面的一些鋪墊,這節會作一個完整的示例。目錄的文件結構以下 :node
- node-express-pug ==>項目目錄 - package.json ==>依賴文件 - server.js ==> 入口文件 - start.js ==> 建立服務 - router.js ==> 路由中轉 - handlers.js ==> 路由處理 - views/home.html ==>首頁 - files/ ==>存放上傳文件的目錄 - node_modules/ ==>依賴項文件目錄
package.json
首先咱們安裝依賴項,本示例中的package.json
文件以下:sql
{ "name": "application-name", "version": "0.0.1", "dependencies": { "formidable": "latest", "mime": "~1.3.4" } }
其中,formidable
用於處理form表單數據,mime
是一個互聯網標準類型,經過設定它就能夠設定文件在瀏覽器的打開方式。稍後咱們會看到如何使用它們。在項目的文件夾下npm install
完成安裝依賴項。express
server.js
首先咱們來實現server.js。在server.js中,咱們主要實現服務器的入口功能,調用各個模塊組件,由其餘模塊實現具體的功能。npm
var server = require('./start'); var router = require('./router'); var handlers = require('./handlers'); var handler = {}; handler[['/','GET']] = handlers.home; handler[['/show','GET']] = handlers.show; handler[['/upload','POST']] = handlers.upload; server.start(router.route,handler);
先導入咱們建立的其餘的模塊文件,而後定義一個handlder對象,這裏配置了對應的路由路徑和其對應的處理方法,相似handler[['/','GET']] = handlers.home;
,而後又調用了server
模塊的start()
方法,並傳入兩個參數,分別是router.route
方法和handler
對象。下面咱們就來實現start.js
文件。json
start.jsstart.js
的主要功能是負責建立一個server服務,而後對請求的路由進行判斷和過濾,交由router
模塊進行處理具體的請求路由。完整的代碼以下瀏覽器
//1.引用模塊 var http = require('http'); var url = require('url'); var formidable = require('formidable'); var querystring = require('querystring'); //2.start(route,handler)方法 //方法的參數是由上層傳遞過來,分別是router.route方法和handler對象 //其中route負責處理路由 //handler對象裏是咱們預先定義的可用處理的路由和對應的請求類型 function start(route,handler) { console.log("Start Begin"); //3.監聽1337端口,建立服務器 var port = process.env.port || 1337; http.createServer(onRequest).listen(port); //4.建立服務器的回調方法 function onRequest(req, res) { console.log("Request Begin"); //解析請求的路徑名 var pathname = url.parse(req.url).pathname; var query = url.parse(req.url).query; //POST方法處理 if (req.method === 'POST'){ //解析form表單POST方式提交數據 var form = new formidable.IncomingForm(); //解析路徑的請求參數,包裝成data向下傳遞 //function (err, fields, files)是解析成功的回調方法 form.parse(req, function (err, fields, files) { if (err){ console.error(err.message); return; } var data = {fields:fields, files:files}; execute(pathname,handler,req, res, data); }); } //GET方法處理請求 if (req.method === 'GET'){ var data = { //解析路徑的請求參數,包裝成data向下傳遞 fields: querystring.parse(query) }; execute(pathname,handler,req, res, data); } console.log("Request End"); } //5.執行處理後的請求 function execute(pathname, handler, req, res, data) { //route執行返回值,若是發生錯誤,統一返回400 var content = route(pathname,handler,req,res,data); if (!content){ res.writeHead('400',{ "Content-Type":"text/plain" }); console.log(req.url); res.write("400 Bad Request"); res.end(); } } } //6.導出模塊 exports.start = start;
這裏注意服務器
var form = new formidable.IncomingForm(); form.parse(req, function (err, fields, files) { });
form.parse
會解析出不少的屬性,傳給回調參數files
,下面是一個我上傳文件後打印的請求參數,參考以下:app
{ fields: {}, files: { fn: File { domain: null, _events: {}, _eventsCount: 0, _maxListeners: undefined, size: 4510, path: 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\upload_5990006963ce4f2 9de485ddadd819355', name: 'sql.sql', type: 'application/octet-stream', hash: null, lastModifiedDate: 2017-03-10T07:00:14.577Z, _writeStream: [Object] } } }
router.js
咱們看到最後的處理交給了var content = route(pathname,handler,req,res,data);
,那麼下面咱們就來看router.js
中route()
方法的實現,完整的router.js
以下:dom
function route(pathname,handler,req,res,data) { console.log('Route'); var method = req.method; if (typeof handler[[pathname,method]] === 'function'){ return handler[[pathname,method]](res, data); }else { console.log("No Method found for " + pathname); return null; } } exports.route = route;
還記得咱們的handler
對象的格式嗎?咱們的參數傳入瞭解析後的pathname
,在這裏又解析了請求的方式var method = req.method;
。typeof handler[[pathname,method]] === 'function'
,若是請求路由和請求不在咱們的handler對象的中,即沒有指定的方法調用,那麼咱們就直接返回null,若是有,就調用這個方法。打個比方handler[['/show','get']](res, data)
等同於handlers.show(res, data)
。
home.html
在home.html中咱們定義了簡單的form表單,實現一個上傳文件的功能。
其中上傳文件的input標籤的name="fn"
.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>HOME</title> </head> <body> <p>This is Home</p> <h1>File Manager</h1> <hr> <a href="/show">Show All Files</a> <form action="/upload" method="post" enctype="multipart/form-data" > <input type="file" name="fn"> <input type="submit" value="Upload File"> </form> </body> </html>
handlers.js
這裏纔是咱們對各個正確路由的響應。如下是完整的代碼,我會逐一解釋說明。
//1.引入模塊 var fs = require('fs'); var path = require('path'); var mime = require('mime'); //2.home方法,即瀏覽器請求`http://127.0.0.1:1337`的方法 //這裏咱們直接讀取views/home.html文件,返回將頁面展示出來 function home(res, data) { fs.readFile('views/home.html', function (err, data) { res.writeHead(200, {"Content-Type": "text/html"}); //注意這裏的data並非home的參數,而是讀取文件成功後的回調data res.write(data); res.end(); }); return true; } //3.show方法 //show方法會處理兩種類型的請求,兩種類型的請求都是get類型 //data.fields && data.fields['fn'] 這裏處理的有上傳的文件後,點擊文件的連接下載文件 function show(res, data) { //解析參數的data的值,這裏用fn是由於後面form表單中定義的值是fn if (data.fields && data.fields['fn']){ //獲取文件名稱 var name = data.fields['fn']; //取得文件完整名稱 var file = path.join(__dirname, '/files', name); //經過文件名指定mime類型 var mimeType = mime.lookup(file); //設定響應頭 res.setHeader('Content-disposition','attachment;filename=' + name); res.setHeader('Content-Type',mimeType); //讀取文件數據 var fileData = fs.readFileSync(file,'binary'); //響應給給用戶 res.end(fileData, 'binary'); //這裏會直接返回,並不會走下面的方法 return true; } //若是用戶不是點擊的下載連接進行show方法 //那麼就讀取文件列表顯示在界面上,而且提供下載功能 fs.readdir('files', function (err, list) { console.log(list); res.writeHead(200, {"Content-Type": "text/html"}); var html = '<html><head></head>' + '<body><h1>File Manager</h1>'; //有文件就生成文件列表 if (list.length) { html += "<ul>"; for (i = 0; i < list.length; i++) { html += '<li><a href="/show?fn=' + list[i] + '">' + list[i] + '</a></li>'; } html += "</ul>"; } else { //沒有文件 html += '<h2>No files found</h2>'; } html += '</body></html>'; res.write(html); res.end(); }); return true; } //4.上傳文件的方法,方法是響應home.html中的form表單 function upload(res, data) { var temp = data.files['fn'].path; var name = data.files['fn'].name; //調用複製文件的方法 copyFile(temp,path.join('./files',name),function (err) { if (err){ console.log(err); return false; }else { return true; } }); } //定義複製文件的方法 function copyFile(source, target, callback) { //讀文件流 var rs = fs.createReadStream(source); rs.on('error',function (err) { callback(err); }); //寫文件流 var ws = fs.createWriteStream(target); ws.on('error',function (err) { callback(err); }); ws.on('finish',function () { callback(); }); //寫入,並覆蓋源文件的內容 rs.pipe(ws); } //導出方法 exports.home = home; exports.show = show; exports.upload = upload;
小結
在項目文件夾下node server
啓動http服務器,在瀏覽器中輸入http://127.0.0.1:1337
在上傳文件後點擊Show All Files
,能夠看到文件列表,點擊其中一個,便可下載。
這樣咱們就完成了一個完整的node處理表單,文件上傳和下載的示例。