zoukankan      html  css  js  c++  java
  • form表单action提交表单,页面不跳转且表单数据含文件的处理方法

    在最近的项目中需要将含 input[type='file']的表单提交给后台 ,并且后台需要将文件存储在数据库中。之前所用的方法都是先将文件上传到七牛服务器上,然后七牛会返回文件的下载地址,在提交表单的时候将文件的下载地址和其他表单元素一起提交即可。但是现在考虑到安全性,这些文件不能上传到七牛服务器上,得直接提交给后台存储到数据库中,在此需要注意以下问题:

      1、提交表单时,如果要提交file,那么form标签里必须使用  enctype="multipart/form-data"  来设置编码。

      2、提交表单时,为了使页面不跳转,引用jquery.form.js插件,使用ajaxSubmit方法提交表单;

      3、提交到nodejs后先将文件上传至node服务器,然后再将文件传给后端。

      下面说一个具体的例子(例子中用了angularjs+nodejs+express):

    第一步:在package.json页面中加入需要的模块,然后npm install进行模块安装,并创建存储上传文件的临时文件夹uploadFiles.

          工程目录结构:

     

     "dependencies": {
        "body-parser": "~1.13.1",
        "cookie-parser": "~1.3.5",
        "debug": "~2.2.0",
        "ejs": "~2.3.2",
        "express": "~4.13.0",
          "express-session": "*",
        "morgan": "~1.6.1",
        "serve-favicon": "~2.3.0",
          "ali-data-mock": "0.1.3",
          "ali-data-proxy-lite": "1.1.16",
          "ccap": "*",
        "fs-extra":"*",//上传文件需要导入的包;
        "formidable":"*",
        "request":"*"
      }

    第二步:html页面

     1 <!----------------------------------编辑支付方式------------------------------------->
     2 <div class="modal fade" id="editChannel" tabindex="-1" role="dialog" aria-labelledby="editTit" aria-hidden="true">
     3     <div class="modal-dialog">
     4         <div class="modal-content">
     5             <form enctype="multipart/form-data" id="upPayChannelForm" method="post" >  
    /****该处设置form的编码方式(multipart/form-data表示有文件需要提交)******/
    6 <div class="modal-header"> 7 <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> 8 <h4 class="modal-title" id="editTit">编辑支付方式</h4> 9 </div> 10 <div class="modal-body"> 11 <div class="form-group"> 12 <p>支付方式<span class="fill_in">(必填)</span></p> 19 <p><input type="text" class="form-control" name="name" value="{{upChannel.name}}" ng-model="upChannel.name" readonly/></p> 20 <p hidden><input type="text" class="form-control" name="type" value="{{upChannel.type}}" ng-model="upChannel.type"/></p> 21 </div> 27 <div class="form-group"> 28 <p>收款账号<span class="fill_in">(必填)</span></p> 29 <p><input type="text" class="form-control" name="account" value="{{upChannel.account}}" ng-model="upChannel.account"/><p> 31 </div> 33 <div class="form-group"> 34 <p>私有密码<span class="fill_in">(必填)</span></p> 35 <p><input type="password" class="form-control" name="privatePassword" value="{{upChannel.privatePassword}}" ng-model="upChannel.privatePassword"/></p> 37 </div> 38 <div class="form-group"> 39 <p>公钥<span class="fill_in">(必填)</span></p> 40 <p><input type="file" name="publickey" id="publicKeyUp" fileread="upChannel.publicKey" accept=".cer,.cex,.crt,.key"/></p> 43 </div> 44 <div class="form-group"> 45 <p>密钥<span class="fill_in">(必填)</span></p> 46 <p><input type="file" name="privateKey" id="privateKeyup" fileread="upChannel.privateKey" accept=".cer,.cex,.crt,.key"/></p> 49 </div> 50 <div class="form-group"> 51 <p>附加内容<span class="fill_in">(必填)</span></p> 52 <p><input type="text" class="form-control" name="addition" value="{{upChannel.addition}}" ng-model="upChannel.addition"/></p> 54 </div> 56 </div> 57 <div class="modal-footer"> 58 <button type="cancel" class="cancel" data-dismiss="modal">返回</button> 59 <button type="button" class="submit createNormalAppBtn" ng-click="updateChannel()">完成</button> 60 </div> 61 </form> 63 </div> 64 </div> 65 </div>
    <script src="http://malsup.github.io/jquery.form.js"></script>//引入jquery.form.js文件,后面service中才能只提交数据,控制页面不跳转
     

    第三步:controller中对表单进行验证:

     1     //编辑支付方式;
     2     $scope.upChannel={
     3         "id":"",
     4         "businessId":"",
     5         "name":"",
     6         "type":"",
     7         "account":"",
     8         "addition":"",
     9         "privatePassword":"",
    10         "userName":"",
    11         "businessName":"",
    12         "locked":"",
    13         "publicKey":"",
    14         "privateKey":""
    15     }
    16  $scope.updateChannel=function(){
    17         //console.log(JSON.stringify($scope.upChannel));
    18         if(checkUpChannel($scope.upChannel)){//表单验证通过后,checkUpChannel($scope.upChannel)函数对表单元素进行一般的验证;
    19                 channel.updateChannel($scope.upChannel).then(function(data){ //验证通过后将表单中非file元素数据提交到service层处理;
    20                     if(data=="success"){
    21                         angular.element("#editChannel").modal("hide");
    22                         getChannelList(1,10);
    23                     }
    24                 },function(err){
    25                     if(err.code=='ECONNREFUSED'){
    26                         Tip(err.code);
    27                     }else{
    28                         Tip(err.statusCode);
    29                     }
    30                 });
    31         }
    32     }

    第四步:service中:

     1   //编辑支付方式
     2        updateChannel:function(upChannel){
     3            var deferred= $q.defer();
     4            var user=JSON.parse(utf8to16(base64decode($cookies.userPay)));//获取cookie
     5 
     6            console.log("service:"+JSON.stringify(upChannel));
     7            //手动提交表单;
     8            var upForm=document.getElementById("upPayChannelForm");
    //设置表单要提交的路径,这里的'/channel/update'是node端配置的路由;
    9 upForm.action='/channel/update?userId='+user.userId+'&businessId='+user.businessId+'&modeId='+upChannel.id+'&name='+upChannel.name+'&type='+upChannel.type;
       //使用jqeury.form.js插件中的ajaxSubmit提交表单,但是页面并不跳转,在一般的表单提交中,如果设置了action属性,那么在提交时页面会自动跳转到action属性所对应的页面中
    10 $("#upPayChannelForm").ajaxSubmit(function(message) { 
    11 // 对于表单提交成功后处理,message为提交页面saveReport.htm的返回内容 12 var data=JSON.parse(message); 13 //console.log(data); 14 if(data.code==200){ 15 return deferred.resolve('success'); 16 }else if(data.code=='ECONNREFUSED'){ 17 return deferred.reject(data); 18 }else{ 19 swal("",data.info,"error"); 20 return deferred.reject(data);//结果正确后返回给controller 21 } 22 }); 23 return deferred.promise; 24 }

     第五步:在app.js文件里配置nodejs路由:

    var express = require('express');
    var path = require('path');
    var favicon = require('serve-favicon');
    var logger = require('morgan');
    var cookieParser = require('cookie-parser');
    var bodyParser = require('body-parser');
    var ejs=require("ejs");
    
    /***********文件上传1**************/
    //var busboy = require('connect-busboy'); //middleware for form/file upload
    
    /********设置nodejs路由对应的文件*************/
    var index = require('./routes/index');
    var ccap=require('./routes/ccap');
    var jiami=require("./routes/jiami");
    var changePwd=require('./routes/changePwd');
    var login=require("./routes/login");
    var business=require("./routes/pay/business");
    var logs=require("./routes/pay/logs");
    var channel=require("./routes/pay/channel");//配置路由
    var config=require("./routes/pay/config");
    
    //var fileUpload=require("./routes/fileUpload");
    /********设置nodejs路由对应的文件*************/
    
    var app = express();
    /**********文件上传1*****************/
    //app.use(busboy());
    /***********文件上传2************/
    //app.use(bodyParser({defer: true}));
    
    // view engine setup
    app.set('views', path.join(__dirname, 'views'));
    //app.set('view engine', 'ejs');
    app.engine('html',ejs.__express);
    app.set('view engine', 'html');
    
    // uncomment after placing your favicon in /public
    //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
    
    
    app.use(logger('dev'));
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: false }));
    app.use(cookieParser());
    app.use(express.static(path.join(__dirname, 'public')));
    
    
    
    
    
    //登录拦截器
    app.use(function (req, res, next) {
    //    var ip=getClientIp(req);
        console.log("获取的cookie是: "+req.cookies.userCookiesPay);
        var url = req.originalUrl;
        var userCookiesPay=req.cookies.userCookiesPay;
        var businessIndex=url.indexOf('/business')!=-1;
        var changePwdIndex=url.indexOf('/changePwd')!=-1;
        var logsIndex=url.indexOf('/logs')!=-1;
        var channelIndex=url.indexOf('/channel')!=-1;
        var configIndex=url.indexOf('/config')!=-1;
        if(url=='/login'&&!(userCookiesPay==undefined)){
            return res.redirect('/');
        }else if((userCookiesPay==undefined)&&(businessIndex||changePwdIndex||logsIndex||channelIndex||configIndex)){
            return res.redirect('/');
        }
        next();
    });
    /***********node路由************/
    app.use('/', index);
    app.use('/ccap',ccap);
    app.use("/app/jiami",jiami);
    app.use("/login",login);
    app.use("/changePwd",changePwd);
    app.use("/business",business);
    app.use("/logs",logs);
    app.use("/channel",channel);
    app.use("/config",config);
    
    //app.use("/fileUpload",fileUpload);
    
    
    
    /***********node路由************/
    
    // catch 404 and forward to error handler
    app.use(function(req, res, next) {
      var err = new Error('Not Found');
      err.status = 404;
      next(err);
    });
    
    // error handlers
    
    // development error handler
    // will print stacktrace
    if (app.get('env') === 'development') {
      app.use(function(err, req, res, next) {
        res.status(err.status || 500);
        res.render('error', {
          message: err.message,
          error: err
        });
      });
    }
    
    // production error handler
    // no stacktraces leaked to user
    app.use(function(err, req, res, next) {
      res.status(err.status || 500);
      res.render('error', {
        message: err.message,
        error: {}
      });
    });
    module.exports = app;

    第六步:最重要的来了,在channel.js文件里进行上传文件和提交表单给后端:

    var express = require('express');
    var router = express.Router();
    var crypto=require('crypto');
    /****导入上传文件所需要的包********/
    var request=require('request');
    var path=require('path');//used for file path
    var formidable = require('formidable'); var fs =require('fs-extra');
    //File System-needed for renaming file etc //编辑支付方式 router.post("/update",function(req,res){
    /**********从action的path地址中获取需要的参数*********************/
    var ip=getClientIp(req); var clientIp=ip.substring(7,ip.length); var businessId=req.query.businessId; var userId=req.query.userId; var modeId=req.query.modeId; var name=req.query.name; var type=req.query.type; var publicName,privateName; //定义一个表单,post存储表单中元素的name和value,为json格式数据;file为表单中选择的文件, var form=new formidable.IncomingForm(); var post={},file={}; form.uploadDir="./../routes/uploadFiles";//表单中文件上传的临时文件的位置 form.encoding="utf-8"; //设置form编码; form.keepExtensions = true; //保留后缀 form.on('error', function(err) { console.log(err); //各种错误 }) //POST:表单中除文件之外的普通数据,不包含文件 field:表单中元素的name value:表单中元素的value值 .on('field', function(field, value) { if (form.type == 'multipart') { //有文件上传时 enctype="multipart/form-data" if (field in post) {//同名表单 checkbox 返回array 同get处理 if (util.isArray(post[field]) === false) { post[field] = [post[field]]; } post[field].push(value); return; } } if(field=='name'){ post[field]=name; }else{ post[field] =value; } if(field=='type'){ post[field]=type; }else{ post[field] =value; } console.log(field+"---"+post[field]); }) .on('file', function(field, file) { //上传文件,对表单中选择的每个文件进行遍历; console.log("表单中的内容:" + JSON.stringify(post)); //console.log(file); //打印上传文件结构 console.log(field);//文件field,即html中 <input type='file' name='XXX'/>中的name属性值; console.log(file.name); //文件名称 console.log(file.size); //文件大小 console.log(file.type); //文件类型 console.log(file.path); //文件路径 if(field=='publickey'){ publicName=file.name; } if(field=='privateKey'){ privateName=file.name; } file[field] = file; //console.log("fiel[" + field + ']=' + file[field]); fs.rename(file.path, form.uploadDir+"/"+file.name, function(err) { //默认会将上传的文件自动生成一个文件名存储在设置的路径下,现在将改文件重新命名为上传文件本身的名称移动到工程目录下; if (err) throw err; }) console.log("参数列表:"+modeId+"-"+userId+"-"+businessId+"-"+clientIp+"-"+post['name']+"-"+post['account']+"-"+post['type']+"-"+post['addition']+"-"+post['privatePassword']); console.log(publicName+"/"+privateName+"/"+(privateName!=undefined&&publicName!=undefined)); if(privateName!=undefined&&publicName!=undefined){ //判断两个文件都上传完成后,开始将表单提交给后端; //console.log(fs.createReadStream(path.join(form.uploadDir+"/", publicName))); //console.log(fs.createReadStream(path.join(form.uploadDir+"/", privateName))); var data={ modeId:modeId, userId:userId, businessId:businessId, ip:clientIp, name:post['name'], account:post['account'], type:post['type'], addition:post['addition'], privatePassword:post['privatePassword'], publicKey:fs.createReadStream(path.join(form.uploadDir+"/", publicName)),//读取上传的文件,并存储在文件流中 privateKey:fs.createReadStream(path.join(form.uploadDir+"/",privateName))//读取上传的文件,并存储在文件流中
    };
    //request.post直接请求后台接口,url:为后台接口,?后面是需要跟的参数,formData:是需要传递的表单数据(有文件的话包含了文件),optionCallback为回调函数,请求成功后所做的处理。 request.post({url:
    'http://10.3.30.117:8888/payplus/internal/v1/pay/business/'+businessId+'/mode/'+modeId+'/update?name='+post['name']+'&account='+post['account']+'&addition='+post['addition']+'&type='+post['type']+'&privatePassword='+post['privatePassword']+'&userId='+userId+'&ip='+clientIp,formData:data},function optionalCallback(err, httpResponse, body){ console.log('upload failed:'+err); //console.log(httpResponse); if (err) { //return console.error('upload failed:', err); console.log('upload failed:'+err); var error=JSON.parse(err); if(error.code=='ECONNREFUSED'){ res.status(error.code).send(error); }else if(error.statusCode==404||error.statusCode==500){ res.status(error.statusCode).send(error); }else{ var responseText=JSON.parse(error.responseText); res.send(responseText); } } console.log('Upload successful! Server responded with:', body); res.send(body);//将后台返回的结果返回给service; }); } }) .on('end', function() { console.log("success"); }); form.parse(req); //解析request对象 }); module.exports = router;

    至此,编辑支付方式的表单就可以成功上传并存储在数库了。

    
    
    
  • 相关阅读:
    4星|《激荡十年,水大鱼大》:过去十年间国内商业简史
    4星|《三联生活周刊》2017年47期:所谓“嬉皮精神”,说白了就是让每一个人都能在不影响其他人的前提下,过自己想要的生活
    3星|《三联生活周刊》2017年48期:联大外文系主任叶公超让学生念一句英语就能判断出学生的籍贯
    3星|《终身成长》:成长型思维让人进步,固定型思维让人固步自封。有新意的励志书,但有锤子模式的嫌疑。
    4星|《癌症新知:科学终结恐慌》:非常新鲜的癌症科普
    PL/SQL Developer使用技巧
    Oracle数据导入导出imp/exp sp2-0734:未知的命令开头'imp...解决方法
    oracle数据库导入导出命令!
    如何完全卸载VS2010
    Oracle命令(一):Oracle登录命令
  • 原文地址:https://www.cnblogs.com/laogai/p/4745326.html
Copyright © 2011-2022 走看看