从项目的角度上来说,图片存储和数据库存储都是必须要分离的,否则这一个模块就能拖垮你的整个工程。 二进制的存储方式,已经淘汰了,性能非常差,在以后的数据库版本里已经取消了这个存储方式。存放路径,是一种非常方便的解决方案,不存在什么其他的问题,容易管理。比如,你以前用二进制存储的一个图片,又要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