• 初学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:
    
    ');
            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="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.最终实现功能

     

  • 相关阅读:
    Elasticsearch学习之SearchRequestBuilder的query类型
    Elasticsearch学习之SearchRequestBuilder常用方法说明
    Elasticsearch学习之head插件安装
    SpringBoot学习之Helloworld
    Http Header里的Content-Type
    柯里化
    VoltDB
    Docker
    PHP框架
    转载: 让我们聊聊Erlang的nif中资源的安全释放
  • 原文地址:https://www.cnblogs.com/caiyishuai/p/12078872.html
Copyright © 2020-2023  润新知