需求分析
一个不能类似微博的论坛网站,有注册,登陆业务,可以发说说,可以修改头像,对于目前许多论坛中有点赞,评论,转发等功能都没有涉及不过在后续的开发中可以自己去加上这些。
数据库的分析
本次开发的使用的是mongdb https://www.mongodb.com/,它是一种非关系性数据库(NoSQL),下图为与关系性数据库的对比:
相比于常见的数据库他有着轻便简洁的特点。
在本次的业务中,主要建立两个集合
users(用于储存用户的信息):昵称,密码,头像地址
posts(用于储存发表的说说):发帖人的昵称,内容,时间
文件的结构:
主要采用mvc的模式开发:
注意:在创建项目之前应先使用npm init来生成package.json
app.js
var express=require("express");
var app=express();
var router=require("./router/router.js");
var session=require("express-session");
//使用session
app.use(session({
secret:'keyboard cat',
resave:false,
saveUninitialized:true,
// cookie:{secure:true}
}));
//模板引擎
app.set("view engine","ejs");
//路由表
app.get("/",router.showIndex); //显示首页
app.get("/regist",router.showRegist); //显示注册服务
app.post("/doregist",router.doRegist); //执行注册,Ajax业务
app.get("/login",router.showLogin); //显示登陆页面
app.post("/dologin",router.doLogin); //执行登陆业务,Ajax业务
app.get("/setavatar",router.showSetavatar); //显示换头像页面
app.post("/dosetavatar",router.doSetavatar); //更换头像
app.get("/cut",router.showcut); //显示裁剪用页面
app.get("/docut",router.docut); //执行裁剪,用ajax太复杂,使用的是表单
app.post("/post",router.dopost); //发表说说
app.get("/getallshuoshuo",router.getAllshuoshuo); //列出所有的说说ajax服务
app.get("/getuserinfo",router.getuserinfo); //查找用户的所有信息
app.get("/getshuoshuoamount",router.getshuoshuoamount);//说说总数
app.get("/user/:user",router.showUser); //显示用户的说说
app.get("/showAlluser",router.showAlluser); //显示所有用户
app.get("/post/:post",router.showUser);
//静态处理
app.use(express.static("./public"));
app.use("/avatar",express.static("./avatar"));
app.listen(3000);
app.js中主要引入了express这个框架,设置了模板引擎,静态文件夹,对路由进行了配置
router.js
router.js中主要对各种业务执行。
引包
var formidable=require("formidable");
var db=require("../models/db.js");
var md5=require("../models/md5.js");
var session=require("express-session");
var path=require("path");
var fs=require("fs");
var gm=require("gm");
渲染主页
exports.showIndex=function(req,res,next){
//检索数据库,查找此人的头像
if(req.session.login=="1") {
//如果登录了
var username=req.session.username;
var login=true;
}else{
//没有登录
var username="";//指定一个空的用户名
var login=false;
}
//已经登录了,那么就要检索数据库,查登录者的头像
db.find("users",{username:req.session.username},function(err,result){
if(result.length=="0"){
var avatar="moren.jpg";
}else{
var avatar=result[0].avatar;
}
res.render("index",{
"login":login,
"username":username,
"active":"首页",
"avatar":avatar //登录人的头像
});
});
注册业务
exports.showRegist=function(req,res,next){
res.render("regist",{
"login":req.session.login=="1"?true:false,
"username":req.session.login=="1"?req.session.username:"",
"active":"注册"
});
}
//注册业务
exports.doRegist=function(req,res,next){
//得到用户填写的东西
var form=new formidable.IncomingForm();
form.parse(req,function(err,fields,files){
//得到表单之后做的事
var username=fields.username;
var password=fields.password;
//console.log(username,password);
//查询数据库中是不是有这个人
db.find("users",{"username":username},function(err,result){
if(err){
res.send("-3");
return;
}
if(result.length!=0){
res.send("-1");
return;
}
//设置MD5加密
password=md5(md5(password)+"waston")
//现在说明没有用户占用
db.insertOne("users",{
"username":username,
"password":password,
"avatar":"moren.jpg"
},function(err,result){
if(err){
res.send("-3");
return;
}
req.session.login="1";
req.session.username=username;
res.send("1"); //注册成功
});
});
//如果没有和这个人 保存这个人
});
}
在注册中对密码的储存十分重要,密码储存进数据库时,一定不能用明码,我们在这儿使用了md5加密
登录业务
exports.showLogin=function(req,res,next){
res.render("login",{
"login":req.session.login=="1"?true:false,
"username":req.session.login=="1"?req.session.username:"",
"active":"登录"
})
}
exports.doLogin=function(req,res,next){
//得到用户表单
//查询数据库看看是否有这个人
//有的话进一步看看这个人的密码是否匹配
var form=new formidable.IncomingForm();
form.parse(req,function(err,fields,files){
//得到表单之后做的事
var username=fields.username;
var password=fields.password;
var jiamihou=md5(md5(password)+"waston");
//查询数据库看看有没有这个人
db.find("users",{"username":username},function(err,result){
if(err){
res.send("-5");
return;
}
if(result.length==0){
//用户不存在
res.send("-1");
return;
}
if( jiamihou==result[0].password){
req.session.login="1";
req.session.username=username;
res.send("1");
return
}else{
//密码错误
res.send("-2")
}
});
});
};
设置头像
//设置头像页面必须保证此时是登陆状态
exports.showSetavatar=function(req,res,next){
//必须保证登录才能有页面
if(req.session.login!="1"){
res.end("非法闯入,这个页面要求登录!!");
return;
}
res.render("setavatar", {
"login": true ,
"username": req.session.login == "1" ? req.session.username : "",
"active": "修改"
})
}
//设置头像
exports.doSetavatar=function(req,res,next){
var form=new formidable.IncomingForm();
form.uploadDir=path.normalize(__dirname+"/../avatar");
form.parse(req,function(err,fields,files){
//console.log(files);
var oldpath=files.touxiang.path;
var newpath=path.normalize(__dirname+"/../avatar")+"/"+req.session.username+".jpg";
fs.rename(oldpath,newpath,function(err){
if(err){
res.send("失败!!");
return;
}
req.session.avatar=req.session.username+".jpg";
//跳转到切得业务
res.redirect("/cut")
})
});
}
exports.showcut=function(req,res,next){
res.render("cutpic",{
avatar:req.session.avatar
});
};
exports.docut=function(req,res,next){
//这几个页面接收几个GET请求参数
//文件名,w,h,x,y
var filename=req.session.avatar
var w=req.query.w;
var h=req.query.h;
var x=req.query.x;
var y=req.query.y;
gm("./avatar/"+filename)
.crop(w,h,x,y)
.resize(100,100,"!")
.write("./avatar/"+filename,function(err){
if(err){
//console.log(err)
res.send("-1");
return;
}
//更改数据库当前用户的avatar这个值
db.updateMany("users",{"username":req.session.username},{
$set:{"avatar":req.session.avatar}},function(err,result){
res.send("1")
});
});
}
设置头像是需保证,必须属于登录状态,所以引入了express-session这个包,来设置session。
在上传头像时也需要保证头像的比例相同所以在前面引包时引入了gm这个包在保证图像的比例相同
在使用时
先上传一张图片
再对图片进行裁剪右边就是真正的头像
发表说说及查看说说
exports.dopost=function(req,res,next){
//必须保证登录
if(req.session.login!="1"){
res.end("非法闯入,这个页面要求登录!!");
return;
}
//用户名
var username=req.session.username;
//得到用户填写的东西
var form=new formidable.IncomingForm();
form.parse(req,function(err,fields,files){
//得到表单之后做的事
var content=fields.content;
//现在说明没有用户占用
db.insertOne("posts",{
"username":username,
"datetime":new Date(),
"content":content
},function(err,result){
if(err){
res.send("-3");
return;
}
res.send("1"); //注册成功
});
});
//如果没有和这个人 保存这个人
}
//列出所有说说
exports.getAllshuoshuo=function(req,res,next){
//这个页面接收参数,页面
var page=req.query.page;
db.find("posts",{},{"pageamount":2,"page":page,"sort":{"datetime":-1}},function(err,result){
res.json(result);
});
};
//列出某个用户信息
exports.getuserinfo=function(req,res,next){
var username=req.query.username;
db.find("users",{"username":username},function(err,result){
var obj={
"username":result[0].username,
"avatar":result[0].avatar,
"_id":result[0]._id,
};
res.json(obj);
});
};
//说说总数
exports.getshuoshuoamount=function(req,res,next){
db.getAllCount("posts",function(count)
{
res.send(count.toString())
});
};
//显示某一个用户的个人主页
exports.showUser=function(req,res,next){
var user=req.params["user"];
db.find("posts",{"username":user},function(err,result){
db.find("users",{"username":user},function(err,result1){
res.render("user",{
"login":req.session.login=="1"?true:false,
"username":req.session.login=="1"?req.session.username:"",
"user":user,
"active":"我的说说",
"myshuoshuo":result,
"mytouxiang":result1[0].avatar
});
});
});
}
//显示所有用户
exports.showAlluser=function (req,res,next) {
db.find("users",{},function(err,result){
// console.log(result)
res.render("alluser",{
"login":req.session.login=="1"?true:false,
"username":req.session.login=="1"?req.session.username:"",
"active":"所有成员",
"alluser":result
});
});
}
models
models中有两个文件db.js(对数据库的操作),md5.js对加密的操作
db.js
//这个模块里面封装了所有对数据库的常用操作
var MongoClient = require('mongodb').MongoClient;
//不管数据库什么操作,都是先连接数据库,所以我们可以把连接数据库
//封装成为内部函数
function _connectDB(callback) {
var url = "mongodb://localhost:27017/shuoshuo"; //从settings文件中,都数据库地址
//连接数据库
MongoClient.connect(url, function (err, db) {
if (err) {
callback(err, null);
return;
}
callback(err, db);
});
}
init();
function init(){
//对数据库进行一个初始化
_connectDB(function(err,db){
if(err){
console.log(err);
return;
}
db.collection('users').createIndex(
{"username":1},
null,
function(err,result){
if(err){
console.log(err)
return;
}
console.log("索引建立成功");
});
});
}
//插入数据
exports.insertOne = function (collectionName, json, callback) {
_connectDB(function (err, db) {
db.collection(collectionName).insertOne(json, function (err, result) {
callback(err, result);
db.close(); //关闭数据库
})
})
};
//查找数据,找到所有数据。args是个对象{"pageamount":10,"page":10}
exports.find = function (collectionName, json, C, D) {
var result = []; //结果数组
if (arguments.length == 3) {
//那么参数C就是callback,参数D没有传。
var callback = C;
var skipnumber = 0;
//数目限制
var limit = 0;
} else if (arguments.length == 4) {
var callback = D;
var args = C;
//应该省略的条数
var skipnumber = args.pageamount * args.page || 0;
//数目限制
var limit = args.pageamount || 0;
//排序方式
var sort = args.sort || {};
} else {
throw new Error("find函数的参数个数,必须是3个,或者4个。");
return;
}
//连接数据库,连接之后查找所有
_connectDB(function (err, db) {
var cursor = db.collection(collectionName).find(json).skip(skipnumber).limit(limit).sort(sort);
cursor.each(function (err, doc) {
if (err) {
callback(err, null);
db.close(); //关闭数据库
return;
}
if (doc != null) {
result.push(doc); //放入结果数组
} else {
//遍历结束,没有更多的文档了
callback(null, result);
db.close(); //关闭数据库
}
});
});
}
//删除
exports.deleteMany = function (collectionName, json, callback) {
_connectDB(function (err, db) {
//删除
db.collection(collectionName).deleteMany(
json,
function (err, results) {
callback(err, results);
db.close(); //关闭数据库
}
);
});
}
//修改
exports.updateMany = function (collectionName, json1, json2, callback) {
_connectDB(function (err, db) {
db.collection(collectionName).updateMany(
json1,
json2,
function (err, results) {
callback(err, results);
db.close();
});
})
}
exports.getAllCount = function (collectionName,callback) {
_connectDB(function (err, db) {
db.collection(collectionName).count({}).then(function(count) {
callback(count);
db.close();
});
})
}
md5.js
var crypto = require("crypto");
module.exports = function(mingma){
var md5 = crypto.createHash('md5');
var password = md5.update(mingma).digest('base64');
return password;
}
node_modules
需要下载的一些模块
ejs:模板引擎,主要用来渲染视图层
express:node的框架
express-session:用来设置session
formidable:用来处理post请求
gm:对图片的裁剪
mongodb:对数据库的操作
public
这个文件夹中主要储存一些静态资源
views
视图层主要用ejs,直接给用户看到的部分可以根据自己的喜爱自己设计这里就不在赘述了