一、项目目的
通过用node学习搭建一个本地相册管理,主要是学习express的基本的接口的使用,以及一些原声node api的调用。目前只提供查看图片以及上传图片的功能,后续会不断改进。
二、所需知识点
1、node 基础api
2、express基础知识以及对中间层有一点了解
3、ejs模版引擎的使用
三、搭建项目
1、新建一个文件夹为little-album
2、然后用npm init初始化,具体填什么,自己做决定,我的如下图。
4、搭建mvc模型(model)-视图(view)-控制器(controller)框架 ,也就是建三个文件夹,m主要是操行一些最底层的数据,v是视图层,主要是显示界面,而c则用来控制一些逻辑,这样的好处显而易见,VC 分层有助于管理复杂的应用程序,因为您可以在一个时间内专门关注一个方面。例如,您可以在不依赖业务逻辑的情况下专注于视图设计。同时也让应用程序的测试更加容易。
5、然后需要一个入口文件app.js ,需要一个放置图片数据的地方uploads 还需要一个放置常用的js和css库,通常起名为public,最后为tempup临时的存放相片的文件。所以整个项目的构成如下如:
6、代码实现中所需要的一些模块
{
"name": "littleablum",
"version": "1.0.0",
"description": "a little album",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"ejs": "^2.5.7",//模版引擎
"express": "^4.13.3",
"formidable": "^1.0.17",//表单上传工具
"silly-datetime": "^0.1.2"//时间转换工具
}
}
7、启动应用,监听3000端口,如下代码实现,首先引入express包,然后引控制器,在设置模版引擎,这时候通过路由中间件引入两个静态文件,然后分别是四个模版引擎页面做路由控制
var express = require('express');
var app = express();
// 控制器
var router = require('./controller')
// 设置模版引擎
app.set("view engine", "ejs");
//路由中间件,静态页面
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)
app.use(function(req, res) {
res.render('err')
})
app.listen(3000)
8、初期总共有四个页面,分别是主页面(index.ejs)、图片展示页面(album.ejs)、图片上传页面(up.ejs)、最后是错误提示页面。页面ui主要是引入了bootstraps的库,简单高效。至于uploads文件夹中的文件可自行创建。图下图建立了三个文件夹以及视图文件夹。9、先通过模型(model)创建两个方式,一个是获取文件夹的名字、另一个是通过文件名获取所在文件夹里所有的图片。
var fs = require("fs");
//这个函数的callback中含有两个参数,一个是err
//另一个是存放所有文件夹名字的array。
exports.getAllAlbums = function(callback){
fs.readdir("./uploads",function(err,files){
if(err){
callback("没有找到uploads文件",null);
}
var allAlbums = [];
(function iterator(i){
if(i == files.length){
//遍历结束
console.log(allAlbums);
callback(null,allAlbums);
return;
}
fs.stat("./uploads/" + files[i],function(err,stats){
if(err){
callback("找不到文件" + files[i] , null);
}
if(stats.isDirectory()){
allAlbums.push(files[i]);
}
iterator(i + 1);
});
})(0);
});
}
//通过文件名,得到所有图片
exports.getAllImagesByAlbumName = function(albumName,callback){
fs.readdir("./uploads/" + albumName,function(err,files){
if(err){
callback("没有找到uploads文件",null);
return;
}
var allImages = [];
(function iterator(i){
if(i == files.length){
//遍历结束
console.log(allImages);
callback(null,allImages);
return;
}
fs.stat("./uploads/" + albumName + "/" + files[i],function(err,stats){
if(err){
callback("找不到文件" + files[i] , null);
return;
}
if(stats.isFile()){
allImages.push(files[i]);
}
iterator(i + 1);
});
})(0);
});
}
10、好了,model已经把数据获取到了,那么controller是不是负责把数据渲染带页面上。总共四个方法,分别是展示uploads文件,展示可以选择的上传文件夹,最后一个是图片的上传。
var file = require("../models/file.js");
var formidable = require('formidable');
var path = require("path");
var fs = require("fs");
var sd = require("silly-datetime");
//首页
exports.showIndex = function(req, res, next) {
//回调函数。把数据当做回调函数的参数来使用。
file.getAllAlbums(function(err, allAlabums) {
if (err) {
next(); //交给下面适合他的中间件
return;
}
res.render("index", {
"albums": allAlabums
});
})
}
//相册页
exports.showAlbum = function(req, res, next) {
//遍历相册中的所有图片
var albumName = req.params.albumName;
//具体业务交给model
file.getAllImagesByAlbumName(albumName, function(err, imagesArray) {
if (err) {
next(); //交给下面的中间件
return;
}
res.render("album", {
"albumname": albumName,
"images": imagesArray
});
});
};
//显示上传
exports.showUp = function(req, res) {
//命令file模块(我们自己写的函数)调用getAllAlbums函数
//得到所有文件夹名字之后做的事情,写在回调函数里面
file.getAllAlbums(function(err, albums) {
res.render("up", {
albums: albums
});
});
};
//上传表单
exports.doPost = function(req, res) {
var form = new formidable.IncomingForm();
form.uploadDir = path.normalize(__dirname + "/../tempup/");
form.parse(req, function(err, fields, files, next) {
console.log(fields);
console.log(files);
//改名
if (err) {
next(); //这个中间件不受理这个请求了,往下走
return;
}
//判断文件尺寸
var size = parseInt(files.tupian.size);
if (size >10240) {
res.send("图片尺寸应该小于10M");
//删除图片
fs.unlink(files.tupian.path);
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("改名失败");
return;
}
res.send("成功");
});
});
return;
}
四、基本的代码实现就这样,接下来就看页面效果展示了。