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