初学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源码: 项 ...
随机推荐
- django项目中form表单和ajax的文件上传功能。
form表单文件上传 路由 # from表单上传 path('formupload/',apply.formupload,name='formupload/'), 方法 # form表单文件上传 de ...
- Virtual DOM--react
Consider a DOM made of thousands of divs. Remember, we are modern web developers, our app is very SP ...
- Generative Adversarial Networks overview(1)
Libo1575899134@outlook.com Libo (原创文章,转发请注明作者) 本文章会先从Gan的简单应用示例讲起,从三个方面问题以及解决思路覆盖25篇GAN论文,第二个大部分会进一步 ...
- linux学习13 Linux运维常用文件管理命令及系统变量基础
一.文件管理命令 1.cp命令,copy a.单源复制,cp [OPTION]... [-T] SOURCE DEST 如果DEST不存在:则事先创建此文件,并复制源文件的数据流至DEST中. 如果D ...
- Numpy | 08 切片和索引
ndarray对象的内容可以通过索引或切片来访问和修改,与 Python 中 list 的切片操作一样. (1)ndarray 数组索引可以基于 0 - n 的下标进行: (2)切片对象可以通过内置的 ...
- spl_autoload_register 和 __autoload()魔术方法
在 PHP 5.3 之前,__autoload 函数抛出的异常不能被 catch 语句块捕获并会导致一个致命错误(Fatal Error). 尽管 __autoload() 函数也能自动加载类和接口 ...
- 【JZOJ6213】【20190613】String
题目 \(n \le 10^{18} \ , \ |T| \le 10^5\) 题解 显然,最少的操作次数一定是贪心地能匹配就匹配 我们可以建出\(T\)的SAM,把SAM不能走的边补到根的后继节点 ...
- nginx之fastcgi配置参数及其缓存
CGI的由来 最早的Web服务器只能简单地响应浏览器发来的HTTP请求,并将存储在服务器上的HTML文件返回给浏览器,也就是静态html文件,但是后期随着网站功能增多网站开发也越来越复杂,以至于出现动 ...
- 64位下的InlineHook
目录 x64下手工HOOK的方法 一丶HOOK的几种方法之远跳 1. 远跳 不影响寄存器 + 15字节方法 2.远跳 影响寄存器 + 12字节方法 3.影响寄存器,恢复寄存器 进行跳转. 4. 常用 ...
- nginx 日志之 error_log
Nginx错误日志平时不用太关注,但是一旦出了问题,就需要借助错误日志来判断问题所在. 配置参数格式:error_log /path/to/log level; Nginx错误日志级别 常见的错误日志 ...