分析论坛项目
- 数据库:总共两个
- users:用户数据,管理用户资料信息;
- 用户名;
- 密码
- 头像
- comment:评论数据,管理用户的评论信息;
- 用户名
- 头像
- 留言内容
- 时间
- users:用户数据,管理用户资料信息;
- 功能
- top部分:--公用的;--include
- 未登录:logo,全部说说,注册,登录;
- 注册功能;跳转注册页面;
- 登录功能;
- 已登录:logo,全部说说,我的说说,用户列表 欢迎您xxx 设置
- 设置功能:
- 个人资料:跳转文件上传页面;图片上传,图片裁切;
- 退出功能;
- 设置功能:
- 未登录:logo,全部说说,注册,登录;
- index页面
- 登录前
- 欢迎注册:通过点击按钮跳转到注册页面
- 欢迎登录:直接登录;
- 登录后
- 左边:头像
- 右边:
- 上面:欢迎你:xxx;
- 留言框:
- 发布留言的按钮;
- 登录前
- 底部评论区
- 无论是否登录,都有留言列表:头像,用户名,内容,时间;
- 我的说说--必须先登录才能看到
- 上面:头像 xxx的个人主页
- 下面:当前登录者的所有留言和留言时间以及删除按钮
- 用户列表
- 上面:用户列表
- 下面:头像和用户名;
- top部分:--公用的;--include
项目制作前的准备
- 项目文件夹及文件
- MVC结构
- M:models => mongodb.js mongodb-setting.js md5.js
- V:views => pub(top.ejs,footer.ejs) cut.ejs index.ejs login.ejs mycom.ejs mycom.ejs reg.ejs upfile.ejs userlist.ejs
- C:controller => router.js package.json;
- mongoDB:数据库
- public:静态资源目录,css,img,js
- avator:头像存储目录,设置静态资源目录;
- node_modules:第三方模块存储;
- package.json:项目初始化文件记录信息;
- app.js:服务器
- readme.txt:文档信息;
- MVC结构
- 安装的第三方模块
- express mongodb ejs silly-datetime gm formidable express-session
- 静态资源文件
- bootstrap jQuery
- 截图的css和js文件
项目制作
-
思路
- 首页的制作,index.ejs文件的制作
- 页面展现前,必须设置静态资源目录,设置在public目录下;用于引入ico图标和js,css文件;
- 渲染页面前设置ejs模板引擎;
- 发送请求:地址栏发送get请求,请求"/forum",然后render渲染首页;
- 提取公共部分,头部和尾部放在top.ejs和footer.ejs文件中,放在views文件在pub中,通过include引入;
- 页面顶部搭建好未登录状态下的静态页面;
- 注册功能
- 通过top顶部中的注册按钮,发送get请求,请求:"/reg",渲染页面;
- 制作静态页面,通过ajax发送post请求,提交注册数据;
- 点击注册页面中的提交按钮,ajax发送post请求,请求:"/doreg",提交数据;
- 管理器router.js中接收数据,用formidable接受post请求发送的数据;通过mongodb封装的函数,插入到数据库的users集合中;
- 插入的数据有:用户名,密码(加密后的密码),默认的图片地址;
- 注意:在插入数据库之前,必须在数据库中查找username是否存在,如果不存在,在插入;
- 插入成功后,必须设置session;设置session的login,username,imgpath;
- 注册成功后,跳转到首页;
window.location.href="/forum"
;
- 登录功能
- 通过top顶部的登录按钮,发送get请求,请求:"/login";渲染页面
- 制作静态页面,通过ajax发送请求,请求:"/dologin";提交登录数据
- 服务器接收数据后,通过db.find()查找是否存在数据,当数据存在的时候,再校验密码;
- 校验成功后,登录成功,但是要设置session,其中imgpath为数据库中存的值;
- 未登录和已登录状态下,页面的显示不同
- 通过ejs中的花式赋值,设置条件判断
- 判断条件是:session设置中的login的赋值;
- 未登录状态,session中的login不存在,或者被赋值为false(在退出登录时);登录状态下,赋值为true;
- 所以通过条件判断,来决定那部分元素显示,那部分元素隐藏,通过curent的设置,来让那个元素点亮;
- 要求:只要涉及到login的值的情况下,在渲染页面时,必须传参;
- 退出登录功能
- 点击设置中的退出登录按钮,通过a标签中的href发送get请求,请求"/logout";
- 服务器中设置session的值,将login设置为false,将username设置为空;
- 通过res.redirect()重定向刷新首页;如:
res.redirect("/forum")
;
- 修改头像—上传头像文件
- 点击设置中的修改头像按钮,通过href发送get请求,请求"/upfile",然后渲染页面;
- 渲染页面之前,要进行判断,判断当前状态是否为登录状态,即session.username是否存在;如果存在,再渲染页面,传入参数,如果不存在,提醒去登录;
- 页面渲染后,通过上传按钮的点击事件,发送ajax的post请求,请求:"/doupFile",上传文件;
- 注意:
1)此时form表单中设置enctype为"multipart/form-data";用于大文件上传;
2)ajax上传file文件,data设置必须为:data:new FormData($('#formid')[0])
,这样才能在服务器端通过files来获取文件信息,与form表单提交相同;
- 注意:
- 上传图片后,通过fs中的rename来修改上传到文件路径和名称;然后放在avatar目录下;
- 上传成功后,先不对数据库中的图片地址进行修改;
- 在前端中ajax的success函数中跳转切图页面;
- 切图功能
- 通过upfile.ejs中ajax请求成功后的success函数中,跳转页面发送请求;请求"/cut";渲染页面
- 在渲染页面前,对session.username进行判断;确保在登录状态下,才能进行裁切;
- 注意:在渲染切图页面时,需要传参,此时的imgpath不能使用session.imgpath,因为此时需要裁切的是刚刚上传上来的图片,不是数据库中的原图片;所以此时需要拿到上传功能中的图片新名称,所以需要设置全局变量;
- 在页面设置中,通过静态目录打开avatar目录下刚刚上传后的图片;
- 引入切图页面的所有文件css和js,放入到public目录下;引入文件;
- 裁切功能
- 点击截图按钮,通过ajax发送get请求,请求:"/docut";提交裁切的数据;
- 裁切数据通过data设置,主要拿的是,裁切后图片的宽,高,左边距,上边距;
- 考虑到,上传的图片大小都不同,所以需要设定显示在页面上的宽度,所以需要算出比例,让参数乘以对应比例后,才能对原图片裁切,否则,尺寸会出错;
- 服务器端拿到数据后,通过gm模块来对原图进行裁切,然后再放在原来的路径下;
- 裁切成功后,对当前用户在数据库中的imgpath进行修改,通过username进行查找,设置imgpath值,需注意的是,使用$set来进行部分设置,否则,就会被全部覆盖;
- 当数据库中imgpath修改成功后,必须对session中的imgpath值进行修改;此时证明切图成功;
- 切图成功后,在success中,跳转回首页;
- 提交留言功能
- 在首页中,制作留言页面;通过判断login的值,在当登录状态下,使其显示;
- 点击提交按钮,发送ajax请求;请求:"/docom",服务器端获取上传的数据,然后插入到数据库的comments集合中;
- 插入的数据:用户名,图像名称,内容,时间戳;参数通过session来拿username和imgpath;
- 插入数据成功后,在success中,刷新首页;
- 留言板制作
- 页面无论在登录还是在未登录的状态下,首页刷新后,就会显示留言信息;
- 需要对数据进行排序和分页显示;
- 获取所有数据的总个数,用于分页;
- 页面一刷新,就通过ajax发送get请求,请求:"/getcount",获取数据总个数,返回success中;
- 设置变量pageamount,每页展示多少个数据,然后计算出页码个数;
- for循环,通过字符串拼接,在DOM插入分页;
- 制作页面结构;封装一个函数getData(n),其中n的值为page值;即哪一页;
- 函数中通过ajax发送请求,请求:"/getcom",获取指定的数据,通过传入page和pageamount来获取指定页码的数据,获取到的数据为一个数组,每一个元素均为一个对象,遍历数组,通过字符串拼接插入DOM中;
- 函数需要传实参n,来获取第几页的数据,从0开始,n为0,则代表第1页;
- 在页面结构制作中,设置了一个显示详情的按钮;
- 点击按钮时,弹出模态框,显示详细信息;
- 注意:模态框中的点击按钮中的
data-target
设置的值必须与弹出框的id值一致,并且要求,每条数据的id值不能相同;可以使用数据自己的"_id"值;
- 首页加载,默认显示第一页的数据,第一个分页点亮
- 在插入DOM后,获取所有分页元素,添加类名active,来点亮第一个;注意:jQuery中无DOM映射,所以需要在页面DOM元素改变后,重新获取元素;
- 调用函数getData(),传入实参为0,即显示第一页数据;
- 点击页码,显示该页码数据
- 对每个页码元素添加点击事件;
- 点击事件触发,通过index()获取该点击元素的索引值,然后作为实参,调用getData()函数,然后获取该页码的数据,插入到页面中,进行更新;然后给该元素添加active类名,将其点亮,其余的元素删除类名;
- 我的说说页面
- 点击top中的我的说说按钮,通过href发送get请求,请求:"/mycom";然后渲染页面;
- 渲染页面时判断是否在登录状态;
- 渲染页面时,需要传入参数:login,username,imgpath,current;
- 页面加载后,通过ajax发送get请求,请求:"/getmycom",获取该用户的所有数据;
- 获取数据后,遍历插入DOM中显示;
- 我的说说页面中每条数据后,设置了删除按钮;
- 点击删除按钮,通过href发送get请求,请求:"/delete/${this._id}";
- 通过每条数据唯一的"_id"值,来发送请求;
- 在服务器端设置get请求的地址为:"/delete/:id";代表id的取值可以是任何值;
- 通过req.params.id来获取请求地址中的id值,也就是获得每条数据的"_id"值;
- 通过db.deleteMany()函数查找删除数据,在查找数据时,需要引入objectId模块,来处理id值;
- 查找的数据:
{_id:objectId(req.params.id)}
; - 删除成功后,在前端页面弹出删除成功,通过window.location.href跳转刷新我的说说页面;
- 用户列表页面
- 用户列表在未登录状态下,就显示,所以,在渲染页面时,无需判断是否在登录状态;
- 点击top中的用户列表按钮,发送get请求,请求:"/userlist",渲染页面,传入参数;
- 页面加载后,通过ajax发送get请求,请求:"/getuserlist",获取users集合下的数据;
- 数据获取后,遍历插入DOM中,页面中需要username和imgpath数据;
- 404页面
- 设置use请求,请求地址为"/",则在输入地址错误时,渲染404页面;
- 首页的制作,index.ejs文件的制作
-
知识点:
- session的设置,在整个项目中,起到决定性作用,登录状态与未登录状态下,通过判断session.login的值,来在ejs中进行条件判断,然后让哪些元素显示;
- 通过获取session.username的值,就能获取到当前登录状态下的用户名,进而去查找数据;
- 所有的操作,就是在刷新首页,当首页刷新时,会通过login的值,来进行判断,然后显示页面元素;进而展现出登录与未登录的状态;
- 在注册和登录的代码中,无论是插入数据还是校验数据,当成功后,都对session的login,username,imgpath进行重新赋值,然后,发送请求"/forum",也就是刷新首页,通过被修改后的值,来判断哪些元素显示,进而呈现出哪种状态;
- 在top顶部导航中控制哪个元素被点亮,通过设置current参数,渲染页面时,传入current值;
- 在前端中发送请求,刷新页面,用到的代码:
window.location.href="/forum"
- 在服务器端发送请求,刷新页面,用到的代码:
res.redirect("/forum")
; - 上传文件时,ajax发送post请求,file文件参数上传时,data必须设置为
data:new FormData($('#formid')[0])
,不能用key=val键值对设置,具体使用请见链接:Ajax通过FormData上传文件 - avatar文件夹中存放的时数据库中存的头像名称所对应的文件;所以需要设置静态资源目录,才能展现这些图片;
- 设置静态资源目录:
app.use("/avatars",express.static("./avatar"));
含义是,在img中src设置时,需要将文件放在avatar目录下,但是在设置时,需要在前面加上"/avatars",然后加上目录下的相对路径;
- 设置静态资源目录:
- icon图标的设置:
<link rel="icon" href="/img/icon.jpg">
,静态资源目录下的相对路径;
-
注意点:
- 只要进行页面渲染时,render中就必须传参;否则,页面不会正常展现;
- 注册和登录时,都适用post请求,安全性更高,get请求,会将用户名和密码显示在地址栏中,不安全;
- 上传文件和截图两个页面在渲染时,必须对session.username进行判断,确定在登录状态下,才能进行,否则,不能进行,提醒登录;
- 头像在数据库中存的是文件名;所以在显示图片时,只需将数据库中的对应地址拿出来,放在src中;但是需注意的是,要设置静态资源目录,要跟use中前面设置的地址一直,并将其放在后面的目录下,在引用时,前面必须加前缀;如此项目中,设置静态资源时,设置了"/avatar",在引用时,必须加此前缀,后面加目录下的相对路径;
- 上传文件后,不对数据库中的imgpath进行修改,必须在截图功能成功后,在能对数据库进行修改;为了防止当上传文件后,不对文件进行裁切,就直接回到首页,然后图片会显示原来的尺寸,页面会很难看,所以,只要不修改数据库,也不能修改session中的imgpath,页面中就不会显示刚刚上传的图片;
- 上传文件的功能:图片在上传时,先上传到uploads文件夹中,只有在裁切图片后,才能放在avatar文件夹下,图片在加载时,静态资源目录也要设置在avatar文件夹下;注:设置静态资源路径时,不要只设置为"/",要设置特定的路径"/avatar";
- 在截图功能中,需要打开图片,所以必须赋值上传功能中的图片新名字;这样才能拿到新图片,不能设置session中的imgpath,这样拿到的是修改头像之前的图片;
- 在数据中的页面详情设置中,使用了模态框,在设置模态框中的点击按钮的
data-target
的值时,要求其必须与弹出框的id值一致,并且要求每条数据的id值不能相同;可以使用数据自己的"_id"值; - 在上传的文件修改名字时,使用fs.rename时,newpath设置时,是通过"./"来获取根目录,然后在设置相对路径;
- 在使用gm()截图时,路径也是通过"./"来获取根目录,然后设置相对路径找文件;
-
项目改善
- 问题:在上传文件后,会将avatar目录下的原图片修改掉,所以不管改不改数据库,或者是否该session.imgpath,此时session.imgpath有可能是新的名字了;所以在刷新首页的时候,上传的文件还会显示,页面会难看;
- 改善:
- 上传文件的存储目录,必须设置一个新的目录,作为中间目录,上传文件修改名字后,放在此目录中;就不会对avatar中的原图片进行覆盖,不截图的情况下,还会显示原来的图片;
- 截图功能中,需要将上传的文件进行打开裁切,可以在设置一个静态资源目录,专门存储上传的文件,然后再cut.ejs中打开上传文件时,就可以写新设置的静态资源目录下的相对路径了;
- 在服务器端切图时,gm中的原地址,写上传文件存储地址,注意此时通过"./"拿到根目录,然后进行相对路径查找,跟静态资源目录的查找不同;在裁切完后,write中的路径需要存储在avatar目录中;覆盖原来图像;
- 此时就完成了修改图像的功能,然后再修改数据库,修改数据库成功后,必须重新设置session.imgpath值;
- 改善:
- 问题:在切图后,会修改图片,进而修改数据库中的图片地址,但是在此数据库中两个集合中,都存了图像的地址,如果需要更改,两个集合都需要修改;所以会出现问题;
- 改善:在users集合中,不再插入 图片地址信息,在页面渲染时,都会传入imgpath值,此时的值为session设置的值,所以,在comments数据库中imgpath数据修改后,需要重新设置session中的imgpath值;
- 问题:在上传文件后,会将avatar目录下的原图片修改掉,所以不管改不改数据库,或者是否该session.imgpath,此时session.imgpath有可能是新的名字了;所以在刷新首页的时候,上传的文件还会显示,页面会难看;