WebStorm下的node.js
一、回顧與繼續
在前面,我們知道了node.js的基本框架和思路,在這些原生環境下我們對node.js的設計思想有了比較深刻的認識,並且具有了編寫大型程序的能力了,但是程序的代碼可能會比較拉雜,因此,我們就需要用到現有的框架了,在我們的社會上,不需要造太多的輪子,而需要在更高層次上去設計‘車’,因此使用別人做好的模板去完成自己的業務是一個比較節省時間並且有創造性的事情,在node.js中我們有太多的輪子,比如說express框架,為我們提供了封裝好的很多做事情的接口,我們可以方便的進行組裝和搭配,比如ejs、jade這些現有的模板引擎,為我們提供了很多可擴展的能力,在這個基礎上我們可以最大限度的發揮自己的創造性,去完成自己業務。
二、Express框架
Express框架是后台的Node框架,所以和jQuery、zepto、yui、bootstrap都不一個東西。Express在后台的受歡迎的程度,和jQuery一樣,就是企業的事實上的標准。原生Node開發,會發現有很多問題。比如:
- 呈遞靜態頁面很不方便,需要處理每個HTTP請求,還要考慮304問題
- 路由處理代碼不直觀清晰,需要寫很多正則表達式和字符串函數
- 不能集中精力寫業務,要考慮很多其他的東西
Express的哲學是在你的想法和服務器之間充當薄薄的一層。這並不意味着他不夠健壯,或者沒有足夠的有用特性,而是盡量少干預你,讓你充分表達自己的思想,同時提供一些有用的東西。同樣的,我們還是使用npm來安裝該框架:--save參數,表示自動修改package.json文件,自動添加依賴項。
1 npm install --save express
用express框架寫一個最簡單的程序:
1 var express=require("express"); 2
3 var app=new express(); 4 app.get("/",function(req,res){ 5 res.send("success!"); 6 }); 7 app.get(/\/student\/([\d]{10})/,function (req,res) { 8 res.send("student info:"+req.params[0]); 9 }) 10 app.listen(3100);
在這里我們首先引用了該框架,其次通過new關鍵字創建了該對象,之后,我們對於首頁的訪問返回成功,對於路徑名為‘/student/十位數字’的訪問,我們通過正則表達式返回了相應的學號,在這里之所以可以使用req的params[0]返回相應的數字,是因為我們在正則表達式中用()對相應的地方進行了選中,同樣的,我們還可以對更長的路徑名進行這樣的過濾,道理是一樣的,同樣的我們可以看到get方法可以對用戶訪問的路徑進行解析,當然post主要是對表單進行處理的,另外在這里除了用正則表達式之外我們還可以使用 : 來對我們想要表達的文字進行表示,比如這里我們就可以寫為“/student/:id”,那么我們就可以得到id的值了,只要再使用正則表達式判斷位數就可以了。
1 app.get("/student/:id",function(req,res){ 2 var id = req.params["id"]; 3 var reg= /^[\d]{6}$/; //正則驗證
4 if(reg.test(id)){ 5 res.send(id); 6 }else{ 7 res.send("請檢查格式"); 8 } 9 });
運行結果如下:
在這里我們還可以使用use方法來暴露靜態文件,也叫靜態文件伺服能力:
app.use(express.static("./public"));
這樣我們就可以在網址中輸入public文件夾下的任何路徑而不需要輸入public,很好的隱藏了一些重要的內容、迷惑了黑客的視線。並且假如public文件夾下有一個a.html文件,我們可以直接用127.0.0.1:3100/a.html來訪問,更強大的是如果public文件夾下有一個index.html文件,則直接輸入127.0.0.1:3100即可以訪問。
a、get用法:
GET請求的參數在URL中,在原生Node中,需要使用url模塊來識別參數字符串。在Express中,不需要使用url模塊了。可以直接使用req.query對象。
當用get請求訪問一個網址的時候,做什么事情:
app.get("網址",function(req,res){
});
這里的網址,不分大小寫,也就是說,路由是
app.get("/AAb",function(req,res){
res.send("你好");
});
實際上小寫的訪問也行。
所有的GET參數,? 后面的都已經被忽略。 錨點#也被忽略,路由到/a , 實際/a?id=2&sex=nan 也能被處理。
表單可以自己提交到自己上。
app.get("/",function(req,res){
res.render("form");
});
適合進行 RESTful路由設計。簡單說,就是一個路徑,但是http method不同,對這個頁面的使用也不同。
/student/345345
get 讀取學生信息
add 添加學生信息
delete 刪除學生信息
1 <!doctype html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <title></title>
6 </head>
7 <body>
8 <form action="#" method="post">
9 <input type="text" name="name"/>
10 <input type="text" name="age"/>
11 <input type="submit"/>
12 </form>
13 </body>
14 </html>
b、post用法:
POST請求在express中不能直接獲得,必須使用body-parser模塊。使用后,將可以用req.body得到參數。但是如果表單中含有文件上傳,那么還是需要使用formidable模塊。當用post訪問一個網址的時候可以使用下面的格式:
app.post("網址",function(req,res){
});
app.post("/",function(req,res){
//將數據添加進入數據庫
res.send("成功");
});
c、all的用法:
如果想處理這個網址的任何method的請求,那么寫all
app.all("/",function(){
});
三、中間件
我們輸入的網址就像一個水流,中間件就像是過濾水流的大網,水流中的某些東西流經某一個地方被阻擋住的話就不會再向下面流去了,因此網的大小(中間件)的前后順序非常重要,如果get、post回調函數中,沒有next參數,那么就匹配上第一個路由,就不會往下匹配了。如果想往下匹配的話,那么需要寫next()。
1 app.get("/",function(req,res,next){ 2 console.log("1"); 3 next(); 4 }); 5 app.get("/",function(req,res){ 6 console.log("2"); 7 });
讓我們看一個例子,下面兩個路由,感覺沒有關系:
1 app.get("/:username/:id",function(req,res){ 2 console.log("1"); 3 res.send("用戶信息" + req.params.username); 4 }); 5 app.get("/admin/login",function(req,res){ 6 console.log("2"); 7 res.send("管理員登錄"); 8 });
但是實際上沖突了,因為admin可以當做用戶名 login可以當做id。
解決方法1:交換位置。
也就是說,express中所有的路由(中間件)的順序至關重要。
匹配上第一個,就不會往下匹配了。 具體的往上寫,抽象的往下寫。
1 app.get("/admin/login",function(req,res){ 2 console.log("2"); 3 res.send("管理員登錄"); 4 }); 5 app.get("/:username/:id",function(req,res){ 6 console.log("1"); 7 res.send("用戶信息" + req.params.username); 8 });
解決方法2: 使用next()
app.get("/:username/:id",function(req,res,next){ var username = req.params.username; //檢索數據庫,如果username不存在,那么next()
if(檢索數據庫){ console.log("1"); res.send("用戶信息"); }else{ next(); } }); app.get("/admin/login",function(req,res){ console.log("2"); res.send("管理員登錄"); });
路由get、post這些東西,就是中間件,中間件講究順序,匹配上第一個之后,就不會往后匹配了。next函數才能夠繼續往后匹配。
app.use()也是一個中間件。與get、post不同的是,他的網址不是精確匹配的。而是能夠有小文件夾拓展的。
比如網址: http://127.0.0.1:3000/admin/aa/bb/cc/dd
1 app.use("/admin",function(req,res){ 2 res.write(req.originalUrl + "\n"); // /admin/aa/bb/cc/dd
3 res.write(req.baseUrl + "\n"); // /admin
4 res.write(req.path + "\n"); // /aa/bb/cc/dd
5 res.end("你好"); 6 });
當不寫路徑的時候,實際上就相當於"/",就是所有網址
app.use(function(req,res,next){ console.log(new Date()); next(); });
app.use()就給了我們增加一些特定功能的便利場所。
大多數情況下,渲染內容用res.render(),將會根據views中的模板文件進行渲染。如果不想使用views文件夾,想自己設置文件夾名字,那么
app.set("views","aaaa");
如果想寫一個快速測試頁,當然可以使用res.send()。這個函數將根據內容,自動幫我們設置了Content-Type頭部和200狀態碼。send()只能用一次,和end一樣。和end不一樣在於能夠自動設置MIME類型。如果想使用不同的狀態碼,可以:
1 res.status(404).send('Sorry, we cannot find that!');
如果想使用不同的Content-Type,可以:
res.set('Content-Type', 'text/html');
四、小小相冊中的大大智慧
講了這么多理論性的東西,我們需要實戰來檢驗自己的掌握程度,因此,在這里我們使用node.js為服務器,制作一個相冊管理器,我們可以在上面上傳相片,也可以查看不同文件夾中的相片,在這里我們使用MVC模型來進行項目的布局、設計和實現!
程序的功能:
1、B/S模式,使用node.js作為服務器,瀏覽器為客戶端,可以遠程訪問,至少保持局域網內的可訪問性。
2、具有圖片的上傳能力,可以將特定的圖片上傳到特定的文件夾中,對上傳圖片的大小有限制,上傳的圖片具有統一格式的命名。
3、具有高效路由能力,可以通過靜態路由功能找到相應文件夾下的圖片並且顯示出來。
4、需要用到bootstrap、express框架、jQuery支持、ejs模板引擎、MVC等多種技術。
首先讓我們看一下工程的目錄:
其中node_modules中至少包含express、ejs、silly-datetime、formidable這些基本的模塊。工程采用了MVC的模式,具有很強的可擴展性,在app.js是整個工程的配置和啟動,在controller中是對app.js中的命令的一種解析和執行,對於不同的請求采用不同的方法進行響應,在models中是對數據的一種管理,比如讀取文件的目錄或者文件夾的目錄等,並且返回這些數據給controller,而controller拿着這些數據交給views去渲染,並且顯示相應的頁面,可以說后端的呈遞完全就是對數據的操作和顯示,這些數據包括簡單的數值、變量、對象、數組、字節流、字符流、多媒體數據等,而前端主要用來進行渲染和展現,在這里我們還是使用ejs模板引擎將得到的數據進行渲染和顯示,並且使用了bootstrap技術和JQuery技術來增加美化程度,更快更好地完善我們的功能。uploads靜態文件夾用來保存上傳過來的圖片,temp文件夾用來間接地呈遞我們上傳的圖片,作為中轉。public靜態文件夾主要存放一下瀏覽器可以直接訪問到的數據,比如css、HTML、圖片、bootstrap等數據和文件,這就是文件的結構了。
4.1、app.js文件
1 var express=require("express"); 2 var app=express(); 3 var router=require("./controller/router.js") 4 app.set("view engine","ejs"); 5
6 app.use(express.static("./public")); 7 app.use(express.static("./uploads")); 8 9 app.get("/",router.showIndex); 10 11 app.get("/up",router.showUp); 12 app.post("/up",router.doPost); 13
14 app.get("/:albumName",router.showAlbum); 15
16 app.use(function (req,res) { 17 res.render("err"); 18 }); 19 app.listen(3100);
這是整個工程的指導文件,對於每一個請求都將通過該文件進行分發和處理,可以說是核心樞紐,實現使用了ejs模板引擎,默認文件夾views為將要渲染的文件夾。其次暴露了兩個文件夾作為靜態文件夾,提供了對這兩個文件夾下的所有文件的路由能力。然后對於首頁訪問命令,直接通過controller層進行細節的處理,通過MVC框架來呈現首頁,然后是對於文件上傳的兩個界面,首先我們需要跳轉到上傳文件界面,這個時候使用get命令即可完成,並且返回填寫信息界面,因此走了一遍MVC,其次是填寫完表單需要提交的時候,我們需要使用post命令來提交表單,並且通過formidable來處理上傳的圖片信息。然后是對於圖片文件夾的訪問,我們對於不同的文件夾呈現不同的圖片列表,最后是對於任意上面處理不了的信息,我們返回錯誤頁面,當然也是通過ejs渲染之后來呈現。然后令程序監聽3000以后的某個端口,避免沖突。
4.2、controller下面的router.js文件
1 var file=require("../models/file.js"); 2 var formidable = require('formidable'); 3 var sd = require("silly-datetime"); 4 var path = require("path"); 5 var fs=require("fs"); 6 //首頁信息
7 exports.showIndex=function(req,res){ 8 file.getAllAlbums(function (err,allAlbums) { 9 if(err){ 10 res.render("err"); 11 return; 12 } 13 res.render("index", 14 {"albums":allAlbums} 15 ); 16 }) 17 } 18 //顯示圖片列表
19 exports.showAlbum=function(req,res){ 20 var albumName=req.params.albumName; 21 file.getAllImagesByAlbumname(albumName,function(err,imageArray){ 22 if(err){ 23 res.render("err"); 24 return; 25 } 26 res.render("album", 27 { 28 "albumName":albumName, "images":imageArray 29 } 30 ); 31 }); 32 } 33 //顯示填寫上傳文件ejs
34 exports.showUp=function(req,res){ 35 file.getAllAlbums(function(err,albums) 36 { 37 res.render("up", { 38 "allAlbums": albums 39 }); 40 }); 41 } 42 //提交上傳的文件並處理,持久化
43 exports.doPost=function(req,res){ 44 var form = new formidable.IncomingForm(); 45 form.uploadDir = "./temp/"; 46 form.parse(req, function (err, fields, files) { 47 console.log(fields); 48 console.log(files); 49 var size=parseInt(files.picture.size); 50 // if(size>1024*1024)
51 // {
52 // res.send("圖片尺寸應小於1M");
53 // //刪除圖片
54 // fs.unlink(files.picture.path);
55 // console.log("刪除成功!")
56 // return;
57 // }
58 var ttt = sd.format(new Date(), 'YYYYMMDDHHmmss'); 59 var ran = parseInt(Math.random() * 89999 + 10000); 60 var extname = path.extname(files.picture.name); 61 var oldpath = __dirname + "/../" + files.picture.path; 62 var newpath = __dirname + "/../uploads/" +fields.folder+"/"+ ttt + ran + extname; 63 console.log(oldpath); 64 console.log(newpath); 65 fs.rename(oldpath, newpath, function (err) { 66 if (err) { 67 throw Error("改名失敗"); 68 console.log("失敗"); 69 return; 70 } 71 res.render("success"); 72 console.log("成功"); 73 return; 74 }); 75 }); 76 }
這個文件可以說是承上啟下,首先分派一個任務交由models層來完成,得到相應的數據,然后拿着這些數據通過res.render()來渲染並且顯示,並且暴露自己,可以讓app.js訪問到。在這里,我們需要注意異步編程的特點,一定要用callback函數來處理那些需要讀寫文件系統或者其他I/O設備的操作,不然將會出現異常和錯誤。
4.3、models文件夾下面的file.js文件
1 var fs = require("fs"); 2 //返回所有文件夾列表
3 exports.getAllAlbums=function(callback){ 4 fs.readdir("./uploads",function (err,files) { 5 if(err){ 6 callback("讀取文件夾失敗!",null); 7 return; 8 } 9 var allAlbums=[]; 10 (function iterator(i) { 11 if(i==files.length) 12 { 13 callback (null,allAlbums); 14 return; 15 } 16 fs.stat("./uploads/"+files[i],function (err,stats) { 17 if(err){ 18 callback("解析文件夾失敗!"+files[i],null); 19 return; 20 } 21 if(stats.isDirectory()) 22 { 23 allAlbums.push(files[i]); 24 } 25 iterator(i+1) 26 }); 27 })(0); 28 }); 29 } 30 //根據文件夾的名字來找到該文件夾下的所有圖片文件並且返回
31 exports.getAllImagesByAlbumname =function(albumname,callback){ 32 fs.readdir("./uploads/"+albumname,function (err,files) { 33 if(err){ 34 callback("讀取文件夾失敗!",null); 35 return; 36 } 37 var allImages=[]; 38 (function iterator(i) { 39 if(i==files.length) 40 { 41 callback (null,allImages); 42 console.log(allImages); 43 return; 44 } 45 fs.stat("./uploads/"+albumname+"/"+files[i],function (err,stats) { 46 if(err){ 47 callback("解析文件失敗"+files[i],null); 48 return; 49 } 50 if(stats.isFile()) 51 { 52 allImages.push(files[i]); 53 } 54 iterator(i+1) 55 }); 56 })(0); 57 }); 58 }
其實我們仔細思考一下就能明白,對文件的數據進行讀,我們只有這兩種操作,一種是返回所有文件夾列表,另一種是根據文件夾的名字來找到該文件夾下的所有圖片文件並且返回,這兩種操作就是完全精細的,適用於我們項目需求的原子操作,可以用來復用。在這里我們使用了iterator來保證同步執行。值得注意的是,如果我們文件讀取失敗或者文件夾讀取失敗,會提示isFile()或者isDirectory()未定義,這個時候,我們需要做的就是仔細看一下文件或者文件夾是否因為路徑的原因而沒有讀取成功。這點在編程中十分重要。還有crtl+alt+I(i),注意這個地方一定是I,而不是L,可以對我們選中的(crtl+A)代碼進行整理,這些編程技巧十分重要!!!!!!
4.4、views文件夾下面的ejs文件
完成了這些我們就需要關注一下前端的實現了,在這里我們通過ejs來進行渲染,同樣的,我們借鑒了bootstrap中的模板,在www.bootcss.com網站中我們可以清楚的找到屬於我們的組件和實例,並且搭建最基本的前端界面,本程序就是從上面借鑒的!我們從這個網站中下載bootstrap,然后解壓之后,放到我們的public文件夾下面去,然后在該網站中找到“起步”,從中下載最簡單的使用代碼,然后在“全局CSS樣式”和“組件”中,我們根據自己的業務要求選擇適合自己使用的控件進行布局,具體的細節這里就略去不提了,最重要的是bootstrap中對jQuery有依賴,因此我們需要從網上下載jQuery的函數庫,就是一個文件而已,然后放到bootstrap文件夾中的js文件夾下,這樣我們就完成了准備工作!!!!!!
准備完成了之后,讓我們看一下ejs代碼,下面的是index.ejs的代碼:
1 <!DOCTYPE html>
2 <html lang="zh-CN">
3 <head>
4 <meta charset="utf-8">
5 <meta http-equiv="X-UA-Compatible" content="IE=edge">
6 <meta name="viewport" content="width=device-width, initial-scale=1">
7 <!-- 上述3個meta標簽*必須*放在最前面,任何其他內容都*必須*跟隨其后! -->
8 <title>小小相冊</title>
9 <link href="css/bootstrap.min.css" rel="stylesheet">
10 <style type="text/css"> 11 .row h4{ 12 text-align:center; 13 } 14 </style>
15 </head>
16 <body>
17
18
19 <nav class="navbar navbar-default">
20 <div class="container-fluid">
21 <!-- Brand and toggle get grouped for better mobile display -->
22 <div class="navbar-header">
23 <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
24 <span class="sr-only">Toggle navigation</span>
25 <span class="icon-bar"></span>
26 <span class="icon-bar"></span>
27 <span class="icon-bar"></span>
28 </button>
29 <a class="navbar-brand" href="#">小小相冊</a>
30 </div>
31
32 <!-- Collect the nav links, forms, and other content for toggling -->
33 <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
34 <ul class="nav navbar-nav">
35 <li class="active"><a href="#">全部相冊 <span class="sr-only">(current)</span></a></li>
36 <li><a href="/up">上傳</a></li>
37 </ul>
38 </div>
39 </div>
40 </nav>
41 <div class="container"> 42 <div class="row"> 43 <% for (var i=0;i<albums.length;i++){%> 44 <div class="col-xs-6 col-md-3"> 45 <a href="<%=albums[i]%>" class="thumbnail"> 46 <img src="images/folder.jpg" alt=""> 47 </a> 48 <h4><%=albums[i]%></h4> 49 </div> 50 <%}%> 51 </div> 52 </div>
53
54 <script src="js/jquery-1.11.3.js"></script> 55 <script src="js/bootstrap.min.js"></script>
56 </body>
57 </html>
同樣的,其他的ejs文件渲染如下:
album.ejs:

1 <!DOCTYPE html>
2 <html lang="zh-CN">
3 <head>
4 <meta charset="utf-8">
5 <meta http-equiv="X-UA-Compatible" content="IE=edge">
6 <meta name="viewport" content="width=device-width, initial-scale=1">
7 <!-- 上述3個meta標簽*必須*放在最前面,任何其他內容都*必須*跟隨其后! -->
8 <title>小小相冊</title>
9 <link href="/css/bootstrap.min.css" rel="stylesheet">
10 <style type="text/css">
11 .row h4{ 12 text-align:center; 13 } 14 </style>
15 </head>
16 <body>
17
18 <nav class="navbar navbar-default">
19 <div class="container-fluid">
20 <!-- Brand and toggle get grouped for better mobile display -->
21 <div class="navbar-header">
22 <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
23 <span class="sr-only">Toggle navigation</span>
24 <span class="icon-bar"></span>
25 <span class="icon-bar"></span>
26 <span class="icon-bar"></span>
27 </button>
28 <a class="navbar-brand" href="#">小小相冊</a>
29 </div>
30
31 <!-- Collect the nav links, forms, and other content for toggling -->
32 <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
33 <ul class="nav navbar-nav">
34 <li ><a href="../">全部相冊 <span class="sr-only">(current)</span></a></li>
35 <li><a href="/up">上傳</a></li>
36 </ul>
37 </div><!-- /.navbar-collapse -->
38 </div><!-- /.container-fluid -->
39 </nav>
40 <ol class="breadcrumb">
41 <li><a href="../">全部相冊</a></li>
42 <li class="active"><%=albumName%></li>
43 </ol>
44 <div class="container">
45 <div class="row">
46 <% for (var i=0;i<images.length;i++){%>
47 <div class="col-xs-6 col-md-3">
48 <a href="#" class="thumbnail">
49 <img src="<%=images[i]%>" alt="">
50 </a>
51 <h4><%=images[i]%></h4>
52 </div>
53 <%}%>
54 </div>
55 </div>
56 <script src="/js/jquery-1.11.3.js"></script>
57 <script src="/js/bootstrap.min.js"></script>
58 </body>
59 </html>
err.ejs:

1 <!DOCTYPE html>
2 <html lang="zh-CN">
3 <head>
4 <meta charset="utf-8">
5 <meta http-equiv="X-UA-Compatible" content="IE=edge">
6 <meta name="viewport" content="width=device-width, initial-scale=1">
7 <!-- 上述3個meta標簽*必須*放在最前面,任何其他內容都*必須*跟隨其后! -->
8 <title>小小相冊</title>
9 <link href="/css/bootstrap.min.css" rel="stylesheet">
10 <style type="text/css">
11 .row h4{ 12 text-align:center; 13 } 14 </style>
15 </head>
16 <body>
17
18
19 <nav class="navbar navbar-default">
20 <div class="container-fluid">
21 <!-- Brand and toggle get grouped for better mobile display -->
22 <div class="navbar-header">
23 <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
24 <span class="sr-only">Toggle navigation</span>
25 <span class="icon-bar"></span>
26 <span class="icon-bar"></span>
27 <span class="icon-bar"></span>
28 </button>
29 <a class="navbar-brand" href="#">小小相冊</a>
30 </div>
31
32 <!-- Collect the nav links, forms, and other content for toggling -->
33 <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
34 <ul class="nav navbar-nav">
35 <li class="active"><a href="../">全部相冊 <span class="sr-only">(current)</span></a></li>
36 <li><a href="/up">上傳</a></li>
37 </ul>
38 </div><!-- /.navbar-collapse -->
39 </div><!-- /.container-fluid -->
40 </nav>
41 <div class="container">
42 <img src="/images/1.jpg" alt="">
43 </div>
44
45 <script src="/js/jquery-1.11.3.js"></script>
46 <script src="/js/bootstrap.min.js"></script>
47 </body>
48 </html>
success.ejs:

1 <!DOCTYPE html>
2 <html lang="zh-CN">
3 <head>
4 <meta charset="utf-8">
5 <meta http-equiv="X-UA-Compatible" content="IE=edge">
6 <meta name="viewport" content="width=device-width, initial-scale=1">
7 <!-- 上述3個meta標簽*必須*放在最前面,任何其他內容都*必須*跟隨其后! -->
8 <title>小小相冊</title>
9 <link href="/css/bootstrap.min.css" rel="stylesheet">
10 <style type="text/css">
11 .row h4{ 12 text-align:center; 13 } 14 </style>
15 </head>
16 <body>
17
18
19 <nav class="navbar navbar-default">
20 <div class="container-fluid">
21 <!-- Brand and toggle get grouped for better mobile display -->
22 <div class="navbar-header">
23 <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
24 <span class="sr-only">Toggle navigation</span>
25 <span class="icon-bar"></span>
26 <span class="icon-bar"></span>
27 <span class="icon-bar"></span>
28 </button>
29 <a class="navbar-brand" href="#">小小相冊</a>
30 </div>
31
32 <!-- Collect the nav links, forms, and other content for toggling -->
33 <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
34 <ul class="nav navbar-nav">
35 <li class="active"><a href="../">全部相冊 <span class="sr-only">(current)</span></a></li>
36 <li><a href="/up">上傳</a></li>
37 </ul>
38 </div><!-- /.navbar-collapse -->
39 </div><!-- /.container-fluid -->
40 </nav>
41 <div class="container">
42 <img src="/images/2.jpg" alt="">
43 </div>
44
45 <script src="/js/jquery-1.11.3.js"></script>
46 <script src="/js/bootstrap.min.js"></script>
47 </body>
48 </html>
up.ejs:
1 <!DOCTYPE html>
2 <html lang="zh-CN">
3 <head>
4 <meta charset="utf-8">
5 <meta http-equiv="X-UA-Compatible" content="IE=edge">
6 <meta name="viewport" content="width=device-width, initial-scale=1">
7 <!-- 上述3個meta標簽*必須*放在最前面,任何其他內容都*必須*跟隨其后! -->
8 <title>小小相冊</title>
9 <link href="/css/bootstrap.min.css" rel="stylesheet">
10 <style type="text/css">
11 .row h4{ 12 text-align:center; 13 } 14 </style>
15 </head>
16 <body>
17
18 <nav class="navbar navbar-default">
19 <div class="container-fluid">
20 <!-- Brand and toggle get grouped for better mobile display -->
21 <div class="navbar-header">
22 <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
23 <span class="sr-only">Toggle navigation</span>
24 <span class="icon-bar"></span>
25 <span class="icon-bar"></span>
26 <span class="icon-bar"></span>
27 </button>
28 <a class="navbar-brand" href="#">小小相冊</a>
29 </div>
30
31 <!-- Collect the nav links, forms, and other content for toggling -->
32 <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
33 <ul class="nav navbar-nav">
34 <li ><a href="../">全部相冊 <span class="sr-only">(current)</span></a></li>
35 <li><a href="/up">上傳</a></li>
36 </ul>
37 </div><!-- /.navbar-collapse -->
38 </div><!-- /.container-fluid -->
39 </nav>
40
41 <div class="container"> 42 <div class="row"> 43 <form style="width:40%" method="post" action="#" enctype="multipart/form-data"> 44 <div class="form-group"> 45 <label for="exampleInputEmail1">選擇文件夾</label> 46 <select class="form-control" name="folder"> 47 <% for(var i=0;i<allAlbums.length;i++){%> 48 <option><%=allAlbums[i]%></option> 49 <%}%> 50 </select> 51 </div> 52 53 <div class="form-group"> 54 <label for="exampleInputFile" >選擇圖片</label> 55 <input type="file" id="exampleInputFile" name="picture"> 56 <p class="help-block">Example block-level help text here.</p> 57 </div> 58 59 <button type="submit" class="btn btn-default">Submit</button> 60 </form> 61 </div> 62 </div>
63 <script src="/js/jquery-1.11.3.js"></script>
64 <script src="/js/bootstrap.min.js"></script>
65 </body>
66 </html>
最后是我們的總依賴package.json,在這里我們對自己的工程進行布局和顯示:

1 { 2 "name": "little-album", 3 "version": "1.0.0", 4 "description": "", 5 "main": "app.js", 6 "dependencies": { 7 "body-parser": "^1.18.2", 8 "ejs": "^2.5.7", 9 "express": "^4.16.1", 10 "formidable": "^1.1.1", 11 "silly-datetime": "^0.1.2" 12 }, 13 "devDependencies": {}, 14 "scripts": { 15 "test": "echo \"Error: no test specified\" && exit 1" 16 }, 17 "author": "", 18 "license": "ISC" 19 }
綜上就是我們的程序了,算是一個demo,我們可以對其進行擴充,我們的架子已經搭好了,以后就是按着這樣的套路來不斷地增磚添瓦,並且對前端進行美工。讓我們來看一下運行效果:
由此可見程序完美執行了相應的任務,完成了相應的功能!!!!!!
工程文件已經壓縮並且放在百度雲上,網址為:http://pan.baidu.com/s/1gfL7UM3, 密碼為:z6qv!!!!!!
六、總結
這也是一篇比較耗時的文章,算是對自己能力的一種錘煉,花了自己一兩天的時間去學習和整理,最后使用一個工程來講自己的所學所思所說所講都用在了實踐中,從最簡單的實踐中,一步步的去粗取精,最后實現了一個文件資源管理器的部分功能,如果想做的話還可以繼續進行,文件夾的創建、修改和刪除、文件的增刪改查、移動到其他文件夾等功能,方法都和上傳類似,到了這里總算可以歇一會兒了,領略一下站在半山腰看着山下的風景的美麗,對於nodejs我們已經掌握的不錯了,但是還遠遠算不上精通,其實沒有那個人可以完全對一門技術精通,即使是創造這門技術的人也沒有絕對的把握去這樣說,但是我們的確已經入門了,並且有了不錯的成果和收獲,將自己從其他語言中學到的MVC等技術運用到這個工程中是一種質的提升和成功,善於總結的人永遠都是最有底氣的人,因為積累了足夠多的錯誤才能更加自信的去面對未知的技術和困難,永遠保持一顆謙遜的心,成功就在眼前!!!!!!