从项目的角度上来说,图片存储和数据库存储都是必须要分离的,否则这一个模块就能拖垮你的整个工程。 二进制的存储方式,已经淘汰了,性能非常差,在以后的数据库版本里已经取消了这个存储方式。存放路径,是一种非常方便的解决方案,不存在什么其他的问题,容易管理。比如,你以前用二进制存储的一个图片,又要IO又要缓存的才能展示给用户看。现在你保存的是地址,仅仅需要把链接提取出来即可,这样减少了数据访问上的压力。
所以要先将图片文件上传到服务器,,再往数据库中存图片文件的路径,怎么传二进制文件呢?form表单可以,但是不是异步的,ajax是异步的,但是要xhr2才支持传二进制文件,所以这里用H5支持的一个对象FormData,相当于模拟一个表单提交将图片传至服务器,当然还是要用到一个插件,formidable,具体流程:
a:cnpm i --save formidable 安装插件,安装好后,它还需要一个配置模块:
var cacheFolder = 'uploadcache/';//存放图片的文件名称
exports.upload = function(req, res) {
var fs = require('fs');
var formidable = require('formidable');
//var currentUser = req.session.username; //
var userDirPath =cacheFolder /*+ currentUser;*/
if (!fs.existsSync(userDirPath)) {
fs.mkdirSync(userDirPath);
}
var form = new formidable.IncomingForm(); //创建上传表单
form.encoding = 'utf-8'; //设置编辑
form.uploadDir = userDirPath; //设置上传目录
form.keepExtensions = true; //保留后缀
form.maxFieldsSize = 2 * 1024 * 1024; //文件大小
form.type = true;
var displayUrl;
form.parse(req, function(err, fields, files) {
if (err) {
res.send(err);
return;
}
var extName = ''; //后缀名
switch (files.upload.type) {
case 'image/pjpeg':
extName = 'jpg';
break;
case 'image/jpeg':
extName = 'jpg';
break;
case 'image/png':
extName = 'png';
break;
case 'image/x-png':
extName = 'png';
break;
}
if (extName.length === 0) {
res.send({
code: 202,
msg: '只支持png和jpg格式图片'
});
return;
} else {
var avatarName = '/' + Date.now() + '.' + extName;
var newPath = form.uploadDir + avatarName;
displayUrl = /*currentUser +*/ avatarName;
fs.renameSync(files.upload.path, newPath); //重命名
res.json({
code: 0,
img: displayUrl
});
}
});
};
b,
前端往服务器存储:
//图片上传
var $imgFile = $(".submit .img input[type=file]");
$imgFile.change(function(){
var type = this.files[0].type;
if(type === "image/png" || type === "image/jpeg"){
var form = new FormData();//H5的对象
form.append("upload", this.files[0]);//第一个参数代表封装的这个对象的key,跟后台协商;
//异步上传图片对象
$.ajax({
url: "/upload",
type: "POST",
dataType:"json",
data: form,
contentType: false, //发送信息到服务器的内容类型 告诉jq不要去设置Content-Type请求头,默认是 application/x-www-form-urlencoded (form类型)
processData: false, //processData 是jq 独有的参数 用于对data参数进行序列化处理,默认值是true,
//所谓序列化 就是比如{ width:1680, height:1050 }参数对象序列化为width=1680&height=1050这样的字符串。
}).done(function(res){
if(!res.code){
$("#img").attr("src", res.img);//显示预览图,到时往数据库存储图片路径时就在这个图片上获取就可以了
}
})
}
})
c.
前端上传完图片,填写完评论内容后提交评论(将图片路径,评论详情保存到数据库),并且收到了后端返回的提交的数据,再次调用模板引擎往html中append这条评论:
//评论提交
var $tit = $("#tit"), $con = $("#con"), $img = $("#img"), $sbt = $("#sbt");
$sbt.click(function(){
var params = {
title: $tit.val(),
content: $con.val(),
img: $img.attr("src"),
author: u
};
if(!params.title || !params.content){
alert("不能为空!");
return;
};
$.post("/sbtComment", params, function(data){
if(!data.code){
if($("h2.empty").is(":visible")){
$("h2.empty").hide();
}
var html = template("commentList", data);
$list.append(html);
$tit.val("");
$con.val("");
$img.attr("src", "");
}
})
})
这是后端的处理:
//评论提交
app.post("/sbtComment", function(req, res){
var comment = new Comment(req.body);
comment.save(function(err, doc){//这个doc就是存储的对象
if(err){return};
res.json({
code: 0,
list: [doc]
})
})
})
d.
修改评论,前端类似的操作:
//评论修改
var $xtit = $("#xtit"), $xcon = $("#xcon"), $ximg = $("#ximg"), $xsave = $("#xsave");
$list.on("click", ".btn-warning", function(){//点击修改弹出模态框同时获取原内容
$xsave.data("id", $(this).data("id"));//将点击的这条评论的id用自定义属性保存到模态框确认按钮上
$xtit.val($(this).parent().prev(".col-md-10").children("h4").text());
$xcon.val($(this).parent().prev(".col-md-10").children("p").text());
$ximg.attr("src", $(this).parent().prev(".col-md-10").find("img").attr("src"));
})
$(".modal-body .ximg input[type=file]").change(function(){//修改图片
var type = this.files[0].type;
if(type === "image/png" || type === "image/jpeg"){
var form = new FormData();//H5的对象
form.append("upload", this.files[0]);//第一个参数代表封装的这个对象的key,跟后台协商;
//异步上传图片对象
$.ajax({
url: "/upload",
type: "POST",
dataType:"json",
data: form,
contentType: false, //发送信息到服务器的内容类型 告诉jq不要去设置Content-Type请求头,默认是 application/x-www-form-urlencoded (form类型)
processData: false, //processData 是jq 独有的参数 用于对data参数进行序列化处理,默认值是true,
//所谓序列化 就是比如{ width:1680, height:1050 }参数对象序列化为width=1680&height=1050这样的字符串。
}).done(function(res){
if(!res.code){
$ximg.attr("src", res.img);
}
})
}
})
$xsave.click(function(){//保存修改
var params = {
title: $xtit.val(),
content: $xcon.val(),
img: $ximg.attr("src"),
_id: $(this).data("id")
};
if(!params.title || !params.content){
alert("不能为空!");
return;
};
$.post("/modifyComment", params, (data)=>{
if(!data.code){
var html = template("commentList", data);
$(".list-group").find("input[data-id="+$(this).data("id")+"]").parents(".list-group").replaceWith(html);
$('#myModal').modal('hide');//关闭模态框
}
});
})
后端的处理:
//评论修改
app.post("/modifyComment", function(req, res){
let {_id, title, content, img} = req.body;
//参数3:new取布尔值,true表示返回修改的这个对象,
Comment.findOneAndUpdate({_id}, {title, content, img}, {new: true}, function(err, doc){
if(err){return};
res.json({
code: 0,
list: [doc]
})
})
})
到这里利用前端开发框架express结合mongoose对mongodb数据库的增删改查已经熟悉了
这几页面我传到git上面去了:https://github.com/LuckLin520/express-And-mongoose