暂没有数据库,所以做的也是本地的资源管理器。
总体设计:
app.js:
var express = require("express"); var app = express(); //控制器 var router = require("./controller"); //设置模版引擎 app.set("view engine","ejs"); //路由中间件 //静态页面 app.use(express.static("./public")); app.use(express.static("./uploads")); //get/的时候,上层函数回调的时候传入req,res //首页 app.get("/",router.showIndex); app.get("/:albumName",router.showAlbum); app.get("/up",router.showUp); app.post("/up",router.doPost); //404 app.use(function (req,res) { res.render("err"); }); app.listen(3000);
用express静态出public和uploads文件夹,用来放资源文件,然后分别路由主页,相册文件夹,上传页面,上传表单处理。
然后在控制层的controller来控制前台和后台的交互
router.js:
var file = require("../models/file.js"); var formidable = require("formidable"); var path = require("path"); var fs = require("fs"); var sd = require("silly-datetime"); exports.showIndex = function (req,res,next) { //错误的,传统的思维,不是Node的思维。 // res.render("index",{ // //注意这里异步的,遇到阻塞,会直接呈递模版引擎,所以这种写法是错误的,小函数会没执行完,就呈递了 // "albums" : file.getAllAlbums() // }); //这就是Node.js的编程思维,就是所有的东西,都是异步的 //所以,内层函数,不是return回来东西,而是调用高层函数提供的 //回调函数。把数据当做回调函数的参数来使用。 file.getAllAlbums(function (err,allAlbums) { //这个function就是callback //err是字符串 if(err) { next(); return; } res.render("index",{ "albums" : allAlbums }); }) }; //相册页 exports.showAlbum = function (req,res,next) { //遍历相册中的所有图片 var albumName = req.params.albumName; //具体业务交给model file.getAllImagesByAlbumName(albumName,function (err,imagesArray) { if(err) { next(); return; } res.render("album",{ "albumname" : albumName, "images" : imagesArray }); }); }; exports.showUp = function (req,res) { //命令file模块(我们自己写的函数)调用getAllAlbums函数 //得到所有文件夹名字之后做的事情,写在回调函数里面 file.getAllAlbums(function (err,albums) { res.render("up",{ albums : albums }); }) } //上传表单 exports.doPost = function (req,res) { var form = new formidable.IncomingForm(); // "../"返回上一级 form.uploadDir = path.normalize(__dirname + "/../tempup/"); form.parse(req, function (err, fields, files,next) { console.log(fields); console.log(files); //改名 if(err) { next(); //这个中间件不受理这个请求了,往下走 return; }; //判断文件尺寸 var size = parseInt(files.tupian.size); if(size > 2000000) { res.send("图片尺寸应该小于2M"); //则删除这个文件 fs.unlink("files.tupian.path",function () { }); return; } var wenjianjia = fields.wenjianjia; var oldpath = files.tupian.path ; //还是加时间戳 var ttt = sd.format(new Date(), "YYYMMDDHHmmss"); var ran = parseInt(Math.random() * 89999 + 10000); var extname = path.extname(files.tupian.name); var newpath = path.normalize(__dirname + "/../uploads/" + wenjianjia + "/" + ttt + ran + extname); fs.rename(oldpath,newpath,function (err) { if(err) { res.send("改名失败"); return; } res.send("成功"); }); }); }
底层的真正处理的模型层的file.js,注意的是由于这里没用es6的先进写法,所以很多是异步语句,正常的return返回是不行的,需要递归迭代来获得所有数据,用callback回调处理
var fs = require("fs"); //这个函数的callback中含有两个参数,一个是err //另一个是存放所有文件夹名字的array。 exports.getAllAlbums = function (callback) { fs.readdir("./uploads", function (err,files) { if(err) { callback("没有找到upload文件",null); } var allAlbums = []; (function iterator(i) { if (i == files.length) { //遍历结束 console.log(allAlbums); //执行完了之后,执行回调函数 callback(null,allAlbums); return; } fs.stat("./uploads/" + files[i], function (err, stats) { if(err) { callback("找不到文件" + files[i],null); } if (stats.isDirectory()) { allAlbums.push(files[i]); } iterator(i + 1); }); })(0); }); } //通过文件名,得到所有图片 exports.getAllImagesByAlbumName = function (albumName,callback) { fs.readdir("./uploads/" + albumName, function (err,files) { if(err) { callback("没有找到upload文件",null); return; } var allImages = []; (function iterator(i) { if (i == files.length) { //遍历结束 console.log(allImages); //执行完了之后,执行回调函数 callback(null,allImages); return; } fs.stat("./uploads/" + albumName + "/" + files[i], function (err, stats) { if(err) { callback("找不到文件" + files[i],null); return; } if (stats.isFile()) { allImages.push(files[i]); } iterator(i + 1); }); })(0); }); }
剩下的就是view视图层的前端样式了,用的ejs模板和bootstrap样式:
主页index.js:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>littleAlbum</title> <link href="css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script> <script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script> <style type="text/css"> .row h4 { text-align: center; } </style> </head> <body> <nav class="navbar navbar-default"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">小小相册</a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="/">全部相册 <span class="sr-only">(current)</span></a></li> <li><a href="up">上传</a></li> <li class="dropdown"> <ul class="dropdown-menu"> <li><a href="#">Action</a></li> <li><a href="#">Another action</a></li> <li><a href="#">Something else here</a></li> <li role="separator" class="divider"></li> <li><a href="#">Separated link</a></li> <li role="separator" class="divider"></li> <li><a href="#">One more separated link</a></li> </ul> </li> </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> <div class="container"> <div class="row"> <% for(var i = 0; i < albums.length; i++){ %> <div class="col-xs-6 col-md-3"> <a href="<%= albums[i] %>" class="thumbnail"> <img src="images/wenjianjia2.jpg" alt="..."> </a> <h4><%= albums[i] %></h4> </div> <% } %> </div> </div> <!--运行之后views和public是在一起的--> <script src="js/jquery-1.11.3.min.js"></script> <script src="js/bootstrap.min.js"></script> </body> </html>
相册页album.ejs:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>littleAlbum</title> <link href="/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script> <script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script> <style type="text/css"> .row h4 { text-align: center; } </style> </head> <body> <nav class="navbar navbar-default"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">小小相册</a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="/">全部相册 <span class="sr-only">(current)</span></a></li> <li><a href="/up">上传</a></li> <li class="dropdown"> <ul class="dropdown-menu"> <li><a href="#">Action</a></li> <li><a href="#">Another action</a></li> <li><a href="#">Something else here</a></li> <li role="separator" class="divider"></li> <li><a href="#">Separated link</a></li> <li role="separator" class="divider"></li> <li><a href="#">One more separated link</a></li> </ul> </li> </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> <div class="container"> <ol class="breadcrumb"> <li><a href="/">全部相册</a></li> <li class="active"><%= albumname %></li> </ol> <div class="row"> <% for(var i = 0; i < images.length; i++){ %> <div class="col-xs-6 col-md-3"> <a href="#" class="thumbnail"> <img src="<%= images[i] %>" alt=""> </a> <h4></h4> </div> </div> <% } %> </div> <!--运行之后views和public是在一起的--> <script src="/js/jquery-1.11.3.min.js"></script> <script src="/js/bootstrap.min.js"></script> </body> </html>
up.ejs:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>小小相册</title> <link href="/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script> <script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script> <style type="text/css"> .row h4 { text-align: center; } </style> </head> <body> <nav class="navbar navbar-default"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">小小相册</a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="/">全部相册 <span class="sr-only">(current)</span></a></li> <li><a href="up">上传</a></li> <li class="dropdown"> <ul class="dropdown-menu"> <li><a href="#">Action</a></li> <li><a href="#">Another action</a></li> <li><a href="#">Something else here</a></li> <li role="separator" class="divider"></li> <li><a href="#">Separated link</a></li> <li role="separator" class="divider"></li> <li><a href="#">One more separated link</a></li> </ul> </li> </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> <div class="container"> <div class="row"> <form style = "width: 40%" method="post" enctype="multipart/form-data" action = "#"> <div class="form-group"> <label for="exampleInputFile">选择文件夹</label> <select class="form-control" name = "wenjianjia"> <% for(var i = 0; i < albums.length; i++) {%> <option><%= albums[i] %></option> <%}%> </select> </div> <div class="form-group"> <label for="exampleInputFile">选择图片</label> <p>尺寸小于2M</p> <input type="file" id="exampleInputFile" name = "tupian"> <p class="help-block">Example block-level help text here.</p> </div> <button type="submit" class="btn btn-default">上传</button> </form> </div> </div> <!--运行之后views和public是在一起的--> <script src="/js/jquery-1.11.3.min.js"></script> <script src="/js/bootstrap.min.js"></script> </body> </html>
err.ejs:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>littleAlbum</title> <link href="/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script> <script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script> <style type="text/css"> .row h4 { text-align: center; } </style> </head> <body> <nav class="navbar navbar-default"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">小小相册</a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="/">全部相册 <span class="sr-only">(current)</span></a></li> <li><a href="up">上传</a></li> <li class="dropdown"> <ul class="dropdown-menu"> <li><a href="#">Action</a></li> <li><a href="#">Another action</a></li> <li><a href="#">Something else here</a></li> <li role="separator" class="divider"></li> <li><a href="#">Separated link</a></li> <li role="separator" class="divider"></li> <li><a href="#">One more separated link</a></li> </ul> </li> </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> <div class="container"> <img src="/images/404.jpg" alt=""> </div> <!--运行之后views和public是在一起的--> <script src="/js/jquery-1.11.3.min.js"></script> <script src="/js/bootstrap.min.js"></script> </body> </html>
注意上传模块用的formidable,获得的files,fields对象参数,是和表单标签样式name属性对应的。