用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>

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

 

posted @ 2017-12-26 09:49  傅杰  阅读(1080)  评论(0编辑  收藏  举报