业务简介:
显示文件夹
点击显示相册
上传相册
一, 在主页显示文件夹
首先, 我们要建立以上的文件夹, 其中 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>
- <%=a lbums[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. 最终实现功能
初学 Node.JS express 小案例 -- 小小相册(不涉及数据库, 非常详细)
来源: http://www.bubuko.com/infodetail-3343890.html