初学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="data: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>
4.最终实现功能

初学nodejs express小案例——小小相册(不涉及数据库,非常详细)的更多相关文章
- nodejs+express+mysql 增删改查
之前,一直使用的是nodejs+thinkjs来完成自己所需的项目需求,而对于nodejs中另外一中应用框架express却了解的少之又少,这两天就简单的了解了一下如何使用express来做一些数据库 ...
- nodejs学习篇 (1)webstorm创建nodejs + express + jade 的web 项目
之前简单了解过nodejs,觉得用nodejs来做个网站也太麻烦了,要自己拼html的字符串返回,这能做网站嘛? 最近看到使用jade模板来开发,觉得挺新奇的,于是试了一把,也了解了一些特性,算是个新 ...
- Ubuntu下搭建NodeJS+Express WEB开发框架
Ubuntu下搭建NodeJS+Express WEB开发框架 2012-12-27 15:06 作者: NodeJSNet 来源: 本站 浏览: 2,966 次阅读 我要评论暂无评论 字号: 大 中 ...
- webstorm创建nodejs + express + jade 的web 项目
webstorm创建nodejs + express + jade 的web 项目 前简单了解过nodejs,觉得用nodejs来做个网站也太麻烦了,要自己拼html的字符串返回,这能做网站嘛? 最近 ...
- 用Nodejs+Express搭建web,nodejs路由和Ajax传数据并返回状态,nodejs+mysql通过ajax获取数据并写入数据库
小编自学Nodejs,看了好多文章发现都不全,而且好多都是一模一样的 当然了,这只是基础的demo,经供参考,但是相信也会有收获 今天的内容是用Nodejs+Express搭建基本的web,然后呢no ...
- jquery mobile小案例
---恢复内容开始--- [jquery mobile小案例]效果图如下: 首先先创建一个页面主要使用data-role="page"这个指令,我们给它起个id="pag ...
- MVC 小案例 -- 信息管理
前几次更新博客都是每次周日晚上到周一,这次是周一晚上开始写,肯定也是有原因的!那就是我的 Tomact 忽然报错,无法启动,错误信息如下!同时我的 win10 也崩了,重启之后连 WIFI 的标志也不 ...
- nodejs+express+socket.io
其实官网文档清楚了 https://socket.io/get-started/chat/ 但是因为之前写的是nodejs+express, socket.io是后加的, 还是有小坑 服务器端: 官 ...
- node.js(小案例)_实现学生信息增删改
一.前言 本节内容主要对小案例做一个总结: 1.如何开始搭建小项目 2.路由设计 3.模块应用 4.项目源码以及实现过程github地址: 项目演示如下: 二.主要内容 1.项目的关键性js源码: 项 ...
随机推荐
- 洛谷P3069 [USACO13JAN]牛的阵容Cow Lineup(尺取法)
思路 考虑比较朴素的解法,枚举每个长度为\(k+1\)的区间,然后统计区间中出现次数最多的颜色.这样的话复杂度为\(O(n*k)\)的,显然不行. 观察到统计每个区间中出现次数最多的颜色中,可以只用看 ...
- centos安全加固
设置SSH登录超时时间 /etc/profile export TMOUT=900 设置账户密码策略 /etc/login.defs PASS_MAX_DAYS 180 PASS_MIN_DAYS 0 ...
- git 学习笔记 —— 获取远端分支并修改后提交至远端仓库
笔者最近进行开发过程中,所有参与者的代码需要通过 git 上传到远端仓库中,不同的模块对应不同的 git 分支,不同模块的数据需要从远端仓库中获取.这里记录下笔者从远端仓库中获取分支数据,进行修改,最 ...
- 命令查询windows&Linux系统版本信息
Linux 查询系统名字输入"cat /proc/version",说明正在运行的内核版本uname -rwindows 查询系统名字win+r -> winversyste ...
- 谷歌浏览器打开不了Axure生成的html文件
1.首先要进行翻墙.https://www.google.com 搜索Axure chrome软件 2. 3.安装axure插件即可. 4.管理扩展程序,允许访问文件网址.
- moya与网络编程思想:网络请求的生命周期
请求数据管理的集中化: 请求配置的标注化: 请求管理的函数式参量化: 几个端点: target代表应用端的原始数据; endpoint代表应用端到网络端的中间数据,这个数据可以编辑公用数据header ...
- CF1105D-Kilani and the Game-(多向bfs)
http://codeforces.com/problemset/problem/1105/D 题意:有一片矩阵区域,一开始有多个势力比如1,2,3,4....9,从势力1开始轮流向外扩张,地图上为‘ ...
- micronaut 学习一 基本安装
一般来说,使用框架就是使用类库同时按照框架的类库套路编写代码,但是从越来越复杂的实际 场景来说,cli以及脚手架工具,可以帮助我们简化好多操作. 以下是micronaut cli 工具的安装以及一个简 ...
- vue-element-admin
https://github.com/deadzq/vue-element-admin-1.git vue-element-admin使用. cnpm install npm run dev
- 安装supervisor 失败 :/usr/bin/python: bad interpreter: No such file
以前在安装python 双版本时将python改为了python2所以找不到python,打开那个echo_supervised_conf然后把 #!/usr/bin/python 改为如图就可以了