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;

至此,編輯支付方式的表單就可以成功上傳並存儲在數庫了。

 

 

 

 
        

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM