用node.js实现mvc相册资源管理器
摘要:通过前两天的学习我大概学了了,用formidable模块文件上传,express框架,以及利用fs模块进行文件读取,今天我学习了如何用这些知识来制作相册,暂没有数据库,所以做的也是本地的资源管理器。根据本地存放照片文件夹,通过服务器来进行查看本地照片文件夹和上传照片到该文件夹
一、成品展示:
二、总体设计
三、实现代码
app.js
var express=require("express"); var app=express(); //控制器 var router=require("./controller"); //在package.json里设置了router.js为默认的js //设置模版引擎 app.set("view engine","ejs"); //路由中间件 //静态页面 app.use(express.static('./public')); app.use(express.static('./uploads'));
//get/的时候,上层函数回调的时候传入req,res
//首页 app.get("/",router.showIndex);
app.get("/up", router.showUp);
app.get('/:albumName',router.showAlbum);
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) { //错误的:传统的思维不是node的思维 // res.render("index",{
//注意这里异步的,遇到阻塞,会直接呈递模版引擎,所以这种写法是错误的,小函数会没执行完,就呈递
// "albums":file.getAllAlbums() // }); //这就是node.js的编程思维,就是所有的东西,都是异步的 //所以,内层函数,不是return回来东西,而是调用高层函数提供的 //回调函数。把数据当成回调函数的参数来使用 file.getAllAlbums(function (err,allAlabums) { //这个函数就是callback //err是字符串 if(err){ res.send(err); return ; } res.render("index",{ "albums":allAlabums }); }); } //相册页 exports.showAlbum=function (req,res) { //遍历相册中的所有图片 var albumName=req.params.albumName; //具体业务交给model file.getAllImagesByAlbumName(albumName,function (err,imagesArray) { if(err){ res.send(err); 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"); //上传到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>2000){ res.send("图片尺寸应该小于1M"); //删除图片 fs.unlink(files.tupian.path); return ; }
//加时间戳 var ttt=sd.format(new Date(),"YYYYMMDDHHmmss"); var ran=parseInt(Math.random()*89999+10000); var extname=path.extname(files.tupian.name); var wenjianjia=fields.wenjianjia; var oldpath=files.tupian.path; var newpath=path.normalize(__dirname+"/../uploads/"+wenjianjia+'/'+ ttt + ran + extname); fs.rename(oldpath,newpath,function(err){ if(err){ res.send('改名失败'); return; } res.send("成功"); }); }); return; }
底层的真正处理的模型层的file.js,注意的是由于这里没用es6的先进写法,所以很多是异步语句,正常的return返回是不行的,需要递归迭代来获得所有数据,用callback回调处理
file.js
var fs=require("fs"); //这个函数的callback中含有两个参数,一个是err //另一个是存放所有文件夹名字的array exports.getAllAlbums=function (callback) { fs.readdir("./uploads", function (err, files) { if (err){ callback("没有找到uploads子文件夹",null); } var allAlbums = []; console.log(files); (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("没有找到uploads文件",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.ejs
<!DOCTYPE html> <html> <head> <title>小小相册</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link href="css/bootstrap.min.css" rel="stylesheet" media="screen"> <style> .row h4{ text-align: center; } </style> </head> <body> <!--<h1>小小相册</h1>--> <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> </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/wjj.jpg" alt="..."> </a> <h4><%=albums[i]%></h4> </div> <%}%> </div> </div> <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>小小相册</title> <link href="/css/bootstrap.min.css" rel="stylesheet"> <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><a href="/">全部相册<span class="sr-only">(current)</span></a></li> <li><a href="/up">上传</a></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> <script src="/js/jquery-1.11.3.min.js"></script> <script src="/js/bootstrap.min.js"></script> </body> </html>
上传页:up.ejs
<!DOCTYPE html> <html> <head> <title>小小相册</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link href="/css/bootstrap.min.css" rel="stylesheet" media="screen"> <style> .row h4{ text-align: center; } </style> </head> <body> <!--<h1>小小相册</h1>--> <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="#">上传</a></li> </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> <div class="container"> <div class="row"> <form method="post" action="#" enctype="multipart/form-data"> <div class="form-group"> <label for="exampleInputEmail1">选择文件夹</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> <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">Submit</button> </form> </div> </div> <script src="/js/jquery-1.11.3.min.js"></script> <script src="/js/bootstrap.min.js"></script> </body> </html>
注意上传模块用的formidable,获得的files,fields对象参数,是和表单标签样式name属性对应的。
错误页面:err.ejs
<!DOCTYPE html> <html> <head> <title>小小相册</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link href="/css/bootstrap.min.css" rel="stylesheet" media="screen"> <style> .row h4{ text-align: center; } </style> </head> <body> <!--<h1>小小相册</h1>--> <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="#">上传</a></li> </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> <div class="container"> <img src="<%=baseurl%>/images/404.jpg" alt=""> </div> <script src="/js/jquery-1.11.3.min.js"></script> <script src="/js/bootstrap.min.js"></script> </body> </html>