视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
Nodejs的form验证及图片上传
2020-11-27 19:59:28 责编:小采
文档
 这次给大家带来Nodejs的form验证及图片上传 ,使用Nodejs的form验证及图片上传注意事项有哪些,下面就是实战案例,一起来看一下。

一、form验证

MVC的form验证有三个地方可以做,第一道关就是前端提交之前,第二道关就是在数据保存之前,也就是在controller中做验证,第三道关就是数据保存的时候,也就是如果提交的数据模型不符合实体定义的约束,数据是无法保存的,这是最后一道防线。第一道关主要是依赖于js或者jquery框架,比较常用的是jquery.validate.js。如果是Asp.net MVC 可以自动生成验证规则,这里就不细究了,网上有很多文章。第二层和各自的业务逻辑有关,也需要做一些必要验证,防止前端禁止JavaScript,而提交不合法数据,这里是要讲基于Mongoose的第三层验证。

1.回顾模型定义

我们先回顾一下之前用Mongoose定义的book模型:

var bookSchema = new mongoose.Schema({
 title: { type: String, required: true },
 rating: {
 type: Number,
 required: true,
 min: 0,
 max: 5
 },
 info: { type: String, required: true },
 img: String,
 tags: [String],
 brief: { type: String, required: true },
 ISBN: String,
});

每个属性定义了类型和是否必须,还可以添加min,max,默认值等其他约束。如果提交的模型不满足这些约束,将不能保存成功。相当于Asp.net MVC中的DataAnnotations的作用。后面的form验证就基于此。

2.添加路由

我们需要增加4个路由规则,2个用于添加(一个get,一个post),一个用于删除,一个用于上传图片:

router.get('/book/create', homeController.bookcreateview);
router.post('/book/create', homeController.doBookCreate);
router.delete('/book/:id', homeController.delete);
router.post('/uploadImg', homeController.uploadImg);

基于Express的路由,我们可以创建Restful的路由规则。路由位于app_server文件夹下。

3.添加控制器方法

home.bookcreateview:

module.exports.bookcreateview = function (req, res) {
 res.render('bookCreate', { title: '新增推荐图书' });
};

这里直接是一个get请求,所以直接用render去渲染视图,当然这个bookCreate视图接下来会创建。

doBookCreate:

module.exports.doBookCreate = function (req, res) { var requestOptions, path, postdata;
 path = "/api/book";
 postdata = {
 title: req.body.title,
 info: req.body.info,
 ISBN: req.body.ISBN,
 brief: req.body.brief,
 tags: req.body.tags,
 img: req.body.img,
 rating:req.body.rating,
 };
 requestOptions = {
 url: apiOptions.server + path,
 method: "POST",
 json: postdata,
 };
 request(requestOptions, function (err, response, body) {
 console.log("body.name", body.name, response.statusCode); if (response.statusCode === 201) {
 res.redirect("/detail/"+body._id);
 } 
 else if (response.statusCode == 400 && body.name && body.name == "ValidationError") {
 res.render('bookCreate', { title: '新增推荐图书', error:"val"});
 } else {
 console.log("body.name",body.name);
 info(res, response.statusCode);
 }
 });
};

info:

function info (res, status) { var title, content; if (status === 404) {
 title = "404, 页面没有找到";
 content = "亲,页面飞走了...";
 } else if (status === 500) {
 title = "500, 内部错误";
 content = "尴尬...,发生错误";
 } else {
 title = status + ", 有什么不对劲";
 content = "某些地方可能有些错误";
 }
 res.status(status);
 res.render('info', {
 title : title,
 content : content,
 status: status,
 });
};

View Code

在上一节,我们创建了数据操作的api部分。代码的流程就是先从req中获取到前端传过来的数据,然后用request模块调用api,如果添加成功(状态码是201)就返回到detail页面,如果验证失败,就原路返回,并给出提示。如果错误,交给info方法去处理。

delete:

module.exports.delete = function (req, res) { var requestOptions, path;
 path = "/api/book/" + req.params.id;
 requestOptions = {
 url: apiOptions.server + path,
 method: "delete",
 json: {},
 };
 request(requestOptions, function (err, response, body) { if (response.statusCode == 204) {
 res.json(1);
 } 
 else {
 res.json(0);
 }
 });
};

如果删除成功,返回的状态码是204,然后返回json(1)让前端去处理界面。

4.添加视图

1) 先需要在图书列表的右侧边栏增加一个按钮:

在books视图中修改:

 .col-md-3
 .userinfo
 p stoneniqiu
 a(href='/book/create').btn.btn-info 新增推荐

当用户点击会跳转到/book/create页面

2)新增推荐页面:

extends layout
include _includes/rating
block content
 .row
 .col-md-12.page.bookdetail
 h3 新增推荐书籍 
 .row
 .col-xs-12.col-md-6
 form.form-horizontal(action='',method="post",role="form") - if (error == "val")
 .alert.alert-danger(role="alert") All fields required, please try again
 .form-group
 label.control-label(for='title') 书名
 input#name.form-control(name='title')
 .form-group
 label.control-label(for='info') 信息
 input#name.form-control(name='info') 
 .form-group
 label.control-label(for='ISBN') ISBN
 input#name.form-control(name='ISBN')
 .form-group
 label.control-label(for='brief') 简介
 input#name.form-control(name='brief')
 .form-group
 label.control-label(for='tags') 标签
 input#name.form-control(name='tags')
 .form-group
 label.control-label(for='rating') 推荐指数
 select#rating.form-control.input-sm(name="rating")
 option 5
 option 4
 option 3
 option 2
 option 1
 .form-group
 p 上传图片
 a.btn.btn-info(id="upload", name="upload") 上传图片
 br
 img(id='img')
 .form-group
 button.btn.btn-primary(type='submit') 提交

if语句的地方是用来显示错误提示;图片上传,稍后完整介绍;所以提交页面基本长成这样:

3)Mongoose验证

这个时候没有加前端验证,form可以直接提交。但是node打印出了错误日志,Book validation failed,验证失败。

这是Mongoose给我们返回的验证信息,这时界面上回显示一个提示信息:

这是因为在controller中的处理:

 else if (response.statusCode == 400 && body.name && body.name == "ValidationError") {
 res.render('bookCreate', { title: '新增推荐图书', error:"val"});
 }

以上说明了Mongoose会在数据保存的时候验证实体,如果实体不满足path规则,将不能保存。但至此有三个问题,第一个问题是提示信息不明确,当然我们可以遍历输出ValidatorError;第二个就是,验证错误之后,页面原来的数据没有了,需要再输入一遍,这个我们可以参考Asp.net MVC将模型数据填充到视图中可以解决;第三个问题就是页面前端还没有验证,form直接就可以提交了,这个可以通过简单的Jquery脚本就可以做到;这三点先不细究。继续往下看,如果规范输入,这个时候是可以提交的,提交之后在books页面可以看到:

4)删除

在标题的右侧增加了一个删除符号(books视图中):

 .col-md-10
 p
 a(href="/Detail/#{book._id}")=book.title span.close(data-id='#{book._id}') ×

并添加脚本:

$(".close").click(function() { if (confirm("确定删除?")) { var id = $(this).data("id"); var row = $(this).parents(".booklist");
 $.ajax({
 url: "/book/" + id, method: "delete",
 }).done(function(data) {
 console.log(data);
 row.fadeOut();
 });
 }
});

脚本可以先位于layout视图下方:

 script(src='/javascripts/books.js')

这样,删除完成之后会隐藏当前行。下面解决图片上传问题。

二、图片上传

前面我们在路由里面定义了一个uploadimg方法,现在实现它。一般都涉及两个部分,一个是前台图片的提交,一个是后端数据的处理。

1.uploadimg 方法实现

先需要安装formidable模块。

然后在Public文件下创建一个upload/temp文件夹

脚本:

var fs = require('fs');var formidable = require('formidable');
module.exports.uploadImg = function (req, res) { var form = new formidable.IncomingForm(); //创建上传表单
 form.encoding = 'utf-8'; //设置编辑
 form.uploadDir = './../public/upload/temp/'; //设置上传目录
 form.keepExtensions = true; //保留后缀
 form.maxFieldsSize = 3 * 1024 * 1024; //文件大小
 form.parse(req, function(err, fields, files) {
 console.log(files); if (err) {
 console.log(err); return res.json(0); 
 } for (var key in files) {
 console.log(files[key].path); var extName = ''; //后缀名
 switch (key.type) { case 'image/pjpeg':
 extName = 'jpg'; break; case 'image/jpeg':
 extName = 'jpg'; break; case 'image/png': case 'image/x-png': default:
 extName = 'png'; break;
 } var avatarName = (new Date()).getTime() + '.' + extName; var newPath = form.uploadDir + avatarName; 
 fs.renameSync(files[key].path, newPath); //重命名
 return res.json("/upload/temp/"+ avatarName);
 }
 });
 
};

这个form会自动将文件保存到upLoadDir目录,并以upload_xxxx格式重新命名,所以最后使用fs模块对文件进行重命名。然后返回给前端。

2.前端

我喜欢用插件,前端我用的是plupload-2.1.8,拥有多种上传方式,比较方便。放置在Public文件下。在layout.jade中引用js:

 script(src='/plupload-2.1.8/js/plupload.full.min.js')
 script(src='/javascripts/books.js')

而在bookCreate.jade视图中,修改如下:

 a.btn.btn-info(id="upload", name="upload") 上传图片
 br img(id='img')
 input#imgvalue(type='hidden',name='img',value='')

a标签用来触发上传,img用来预览,input用来存放路径。在books.js下增加以下代码:

var uploader = new plupload.Uploader({
 runtimes: 'html5,flash,silverlight,html4', browse_button: "upload", url: '/uploadImg',
 flash_swf_url: '/plupload-2.1.8/js/Moxie.swf',
 silverlight_xap_url: '/plupload-2.1.8/js/Moxie.xap',
 filters: {
 max_file_size: "3mb",
 mime_types: [
 { title: "Image files", extensions: "jpg,gif,png" },
 { title: "Zip files", extensions: "zip" }
 ]
 },
 init: {
 PostInit: function () {
 },
 FilesAdded: function (up, files) {
 plupload.each(files, function (file) {
 uploader.start();
 });
 },
 UploadProgress: function (up, file) {
 },
 Error: function (up, err) {
 }
 }
});
uploader.init();
uploader.bind('FileUploaded', function (upldr, file, object) { var data = JSON.parse(object.response);
 console.log(data); $("#img").attr("src", data);
 $("#imgvalue").val(data);});

提交:

上传成功后跳转到detail页面。

相信看了本文案例你已经掌握了方法,更多精彩请关注Gxl网其它相关文章!

推荐阅读:

JavaScript的事件管理

jQuery、Angular、node中的Promise详解

JS里特别好用的轻量级日期插件

JavaScript关于IE8兼容问题的处理

下载本文
显示全文
专题