Node 实现一个MVC相册资源管理器
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Node 实现一个MVC相册资源管理器相关的知识,希望对你有一定的参考价值。
暂没有数据库,所以做的也是本地的资源管理器。
总体设计:
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属性对应的。
以上是关于Node 实现一个MVC相册资源管理器的主要内容,如果未能解决你的问题,请参考以下文章