初学nodejs express小案例——小小相册(不涉及数据库,非常详细)

业务简介:

显示文件夹

 点击显示相册

上传相册

 

一、在主页显示文件夹

 首先,我们要建立以上的文件夹,其中views用于放模板ejs,uploads里放的是相册文件夹,public是网页所需要的css,js等,node_modules放的是开发要用到的包,models是为数据库而建立的(本次用不到数据库)里面的函数是最底层的,tempup只是用于图片上传时的中转站(之后会懂的),controller文件夹里就是真正需要实现业务的函数。

 

1.在app.js里使用express

var express = require("express");
var app = express();
//控制器
var router = require("./controller");

//设置模板引擎
app.set("view engine","ejs");

//路由中间件
//静态页面
//app.use("/static",express.static("./public"));//所有/static/是从public下找
app.use(express.static("./public"));
app.use(express.static("./uploads"));

app.get("/",router.showIndex);//函数的引用
app.listen(3000);
这一句表示当开启网页 http://localhost:3000/ 时,将调用router里的showIndex
app.get("/",router.showIndex);//函数的引用 

2. 在router里需要写showIndex函数,函数中,通过调用file,js里的getAllAlbums函数获得allAlbums数组,再将数组给allAlbums,同时渲染前端页面index.ejs,其中ejs可不写

var file = require("../models/file")
//用于文件操作
var fs = require("fs");

//首页
exports.showIndex = function (req,res,next) {
    //传统的思维,错误的
    /*res.render("index",{ //由于异步不能这么写,还没return就已经赋值了
        "albums":file.getAllAlbums()
    });*/
    //这就是Node.js的编程思维,就是所有东西都是异步的
    //所以,内侧函数,不是return回来东西,而是调用高层函数
    //提供的回调函数,把数据当作回调函数的参数来使用。
    file.getAllAlbums(function (err,allAlbums) {
        if(err){
            next();//交给下面适合它的中间件
            //res.render("err");
            return;
        }
        res.render("index",{
            "albums":allAlbums
        })
    })
}

 3.接着我们在models文件下建立file.js,在里面写getAllAlbums函数,用于获取uploads文件夹下的所有文件夹,借用迭代器组成一个数组allAlbums返回。

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);
                //return allAlbums;//遍历结束
                callback(null,allAlbums);
                return;
            }
            fs.stat("./uploads/"+files[i],function (err,stats) {
                if(err){
                    callback("找不到文件"+files,null);
                }
                if(stats.isDirectory()){
                    allAlbums.push(files[i]);
                }
                iterator(i +1);
            })
        })(0);
    });
}

 4.最后,要写模板函数index.ejs,在views下新建一个index.ejs,利用bootstrap写模板,这里时关键。因为我们已将public静态了,也就是public里的东西都公开了。所以这里直接images/图片即可

<!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">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>小小相册</title>

    <!-- Bootstrap -->
    <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 class="active"><a href="/">全部相册 <span class="sr-only"></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>


<!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
<script src="js/jquery.min.js"></script>
<!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 -->
<script src="js/bootstrap.min.js"></script>
</body>
</html>

 

二、404页面的制作

1.在views下新建一个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">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>小小相册</title>

    <!-- Bootstrap -->
    <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"></span></a></li>
                <li><a href="/up">上传</a></li>
            </ul>
        </div><!-- /.navbar-collapse -->
    </div><!-- /.container-fluid -->
</nav>

<div class="container">
    <img src="/images/404.gif"/>
</div>


<!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
<script src="/js/jquery.min.js"></script>
<!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 -->
<script src="/js/bootstrap.min.js"></script>
</body>
</html>

2.再在app.js下配置路由

 

 

三、点击相册文件夹,显示所有图片

1.先配置路由

app.get("/:albumName",router.showAlbum);

2.在router.js里写函数showAlbum,要通过向file.js里写函数getAllImagesByAlbumName传相册名获得该相册的所有图片路径,再传给前端album.ejs

//相册页
exports.showAlbum = function (req,res,next) {
    //遍历相册页的所有图片
    var albumName = req.params.albumName;
    //具体业务交给model

    //调用函数得到图片
    file.getAllImagesByAlbumName(albumName,function(err,imagesArray){
        //返回得到imagesArray
        if(err){
            next();//交给下面适合它的中间件
            //res.render("err");
            return;
        }
        //渲染album.ejs页面,把albumname赋值albumName传到页面
        res.render("album",{
            "albumname":albumName,
            "images":imagesArray
        });
    });
}

3.在models里的file.js里写函数getAllImagesByAlbumName,利用router里传来的相册名,获取所有图片路径

//通过文件名,得到所有图片
exports.getAllImagesByAlbumName = function (albumName,callback) {
    fs.readdir("./uploads/"+albumName,function (err,files) {
        if(err){
            callback("没有找到uploads文件夹",null);
            return;
        }
        var allImages = [];
        //console.log(files);//[ '小狗', '军犬' ]

        //迭代器 异步
        (function iterator(i) {
            if(i == files.length){
                //console.log(allImages);
                //return allAlbums;//遍历结束
                callback(null,allImages);
                return;
            }
            fs.stat("./uploads/"+albumName+"/"+files[i],function (err,stats) {
                if(err){
                    callback("找不到文件"+files,null);
                    return;
                }
                if(stats.isFile()){
                    allImages.push(files[i]);
                }
                iterator(i +1);
            })
        })(0);
    })
}

4.写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">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>小小相册</title>

    <!-- Bootstrap -->
    <link href="/css/bootstrap.min.css" rel="stylesheet">
    <style type="text/css">
        .row h4{
            text-align: center;
        }
        .thumbnail img{
            width:auto;
            height: auto;
        }
    </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"></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>
            </div>
        <%}%>
    </div>
</div>


<!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
<script src="/js/jquery.min.js"></script>
<!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 -->
<script src="/js/bootstrap.min.js"></script>
</body>
</html>

5.记得最后把超链接都补补全

 

四、上传相册

1.先配置路由,做一个上传的界面

app.js全部代码:

var express = require("express");
var app = express();
//控制器
var router = require("./controller");

//设置模板引擎
app.set("view engine","ejs");

//路由中间件
//静态页面
//app.use("/static",express.static("./public"));//所有/static/是从public下找
app.use(express.static("./public"));
app.use(express.static("./uploads"));

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);

2.在router里写showUp和doPost函数

showUp比较简单,就是跳转到up.ejs页面,同时该页面有个下拉框,需要显示所有相册文件夹的名字

 doPost比较复杂,它先将上传的文件放到了tempup文件夹里,然后利用fs自带函数rename改名,新名字使用了上传的时间戳。改名的同时,可以更改文件路径。再将文件上传的过程中,先判断图片的大小有没有超限,超的话使用fs自带的unlink函数删除。

  

router.js全部代码

var file = require("../models/file")
//npm install silly-datetime 用于上传使用
var formidable = require('formidable');
var path = require("path");
//用于文件操作
var fs = require("fs");
//npm install silly-datetime 用于获取日期
var sd = require("silly-datetime");

//首页
exports.showIndex = function (req,res,next) {
    //传统的思维,错误的
    /*res.render("index",{ //由于异步不能这么写,还没return就已经赋值了
        "albums":file.getAllAlbums()
    });*/
    //这就是Node.js的编程思维,就是所有东西都是异步的
    //所以,内侧函数,不是return回来东西,而是调用高层函数
    //提供的回调函数,把数据当作回调函数的参数来使用。
    file.getAllAlbums(function (err,allAlbums) {
        if(err){
            next();//交给下面适合它的中间件
            //res.render("err");
            return;
        }
        res.render("index",{
            "albums":allAlbums
        })
    })
}

//相册页
exports.showAlbum = function (req,res,next) {
    //遍历相册页的所有图片
    var albumName = req.params.albumName;
    //具体业务交给model

    //调用函数得到图片
    file.getAllImagesByAlbumName(albumName,function(err,imagesArray){
        //返回得到imagesArray
        if(err){
            next();//交给下面适合它的中间件
            //res.render("err");
            return;
        }
        //渲染album.ejs页面,把albumname赋值albumName传到页面
        res.render("album",{
            "albumname":albumName,
            "images":imagesArray
        });
    });
}

exports.showUp = function (req,res) {
    //调用file的getAllAlbums函数,得到文件夹名字之后的事情卸载回调函数里
    file.getAllAlbums(function (err,allAlbums) {
        if(err){
            next();//交给下面适合它的中间件
            //res.render("err");
            return;
        }
        res.render("up",{
            "albums":allAlbums
        })
    })
}

//上传表单
exports.doPost = function (req,res) {
    var form = new formidable.IncomingForm();

    form.uploadDir = path.normalize(__dirname + "/../tempup/");
    console.log(__dirname + "/../temup/")

    form.parse(req,function (err,fields,files) {
        console.log(fields);
        console.log(files);
        /*res.writeHead(200,{'content-type':'text/plain'});
        res.write('received upload:\n\n');
        res.end(util.inspect({fields: fields,files:files}));*/
        //改名
        if(err){
            next(); //这个中间件不受理这个请求了,往下走
            return;
        }
        //判断文件尺寸
        var size = parseInt(files.tupian.size);
        if(size>102400){
            //console.log("图片尺寸应该小于100M");
            res.send("图片尺寸应该小于100M");
            //删除图片
            fs.unlink(files.tupian.path,function(){});//新版本要加function(){}
            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("改名失败");
                //console.log("改名失败!")
                return;
            }
            res.send("成功");
        });
    });
}

3.写up.ejs,在views里新建up.ejs

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">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>小小相册</title>

    <!-- Bootstrap -->
    <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"></span></a></li>
                <li class="active"><a href="/up">上传</a></li>
            </ul>
        </div><!-- /.navbar-collapse -->
    </div><!-- /.container-fluid -->
</nav>

<div class="container">
    <div class="row">
        <form style="width:40%;" 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">
            </div>
            <button type="submit" class="btn btn-default">上传</button>
        </form>
    </div>
</div>


<!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
<script src="js/jquery.min.js"></script>
<!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 -->
<script src="js/bootstrap.min.js"></script>
</body>
</html>
View Code

 

 4.最终实现功能

 

posted on 2019-12-22 01:25  蔡军帅  阅读(626)  评论(0编辑  收藏  举报