項目背景:有兩個服務器,一個是用戶操作的手機端服務器,一個是後臺(響應前一個服務器,並能訪問數據庫)
項目需求:用戶從手機端上傳身份證以及個人信息,我們需要將數據傳給後臺,並且在信息認證通過後將身份證照片存在後臺服務器的D盤文件夾下。
需要的jar包:commons-fileupload-1.3.3.jar ; commons-io-2.6.jar
下載鏈接:www.baidu.com
哈哈哈,皮了一下,真正的下載方式是:百度那兩個jar包名稱,去官網下,是免費的(狗頭)
jqueryForm上傳數據是利用的表單提交,下面是html頁面的form:
<form id="form1" action="../registerRealInfo" method="post" enctype="multipart/form-data" class="mui-input-group">
<input type="hidden" id="loginName" name="loginName"/>
<input type="hidden" id="loginPwd" name="loginPwd"/>
<div class="mui-input-row">
<label id="reName">真實姓名</label>
<input type="text" id="realName" name="realName" autocomplete="off" class="dev-input" placeholder="請輸入身份證上的真實姓名" value="" />
</div>
<div class="mui-input-row">
<label id="reNum">身份證號碼</label>
<input type="text" id="IDCardNum" maxlength="18" name="IDCardNum" onkeyup="value=value.replace(/[\u4E00-\u9FA5]/g,'')" autocomplete="off" class="dev-input" placeholder="請輸入18位身份證號碼" value="" />
</div>
<div class="title img-title" id="reImage">證件圖片</div>
<ul class="mui-table-view mui-grid-view">
<li class="mui-table-view-cell mui-col-xs-12">
<a href="#">
<div class="image-box">
<input type="file" name="IDCardImage" id="IDCardImage" accept="image/*" class="choseBtn" />
<div id="forsee" class="upImageBox"></div>
</div>
<div class="mui-media-body" id="IdCard">身份證個人信息面</div>
</a>
</li>
</ul>
</form>
下面是js方法,利用jqueryForm的ajaxSubmit可實現異步的表單提交:
$('#comRegister').click(function(){
var options = {
url: "../registerRealInfo", //提交地址:默認是form的action,如果申明,則會覆蓋
type: "post", //默認是form的method(get or post),如果申明,則會覆蓋
beforeSubmit: beforeCheck, //提交前的回調函數
success: function (data) { //提交成功後的回調函數
if(data.result=="success"){
$(location).attr('href','registerSuccessful.html?loginName='+loginName);
}else if(data.result=="true"){
if(data.resultMsg == 1){//身份信息驗證失敗
mui.toast(infoWrong);
}else if(data.resultMsg == 2){//身份證圖片驗證失敗
mui.toast(photoWrong);
}else if(data.resultMsg == 5){//每臺設備每天認證次數限5次,請明天再來
mui.toast(timesLimited);
}
}else if(data.result=="false"){
mui.toast(registerFail);
}else if(data.result=="done"){
mui.toast(alreadyDone);
}
},
error:function (data) {
mui.toast(programError);
},
target: "#output", //把服務器返回的內容放入id爲output的元素中
dataType: "json", //html(默認), xml, script, json...接受服務端返回的類型
clearForm: false, //成功提交後,是否清除所有表單元素的值
resetForm: false, //成功提交後,是否重置所有表單元素的值
timeout: 300000 //限制請求的時間,當請求大於300秒後,跳出請求
};
$('#form1').ajaxSubmit(options);
});
**異步的表單提交的目的:**常規的表單提交,無論後臺如何反饋信息,都會刷新頁面,而jqueryForm的ajaxSubmit既可實現表單提交,也能實現ajax的功能,保證頁面不會刷新,等待後臺返回結果,再進行需要的操作。那麼爲什麼不用ajax呢,因爲ajax不能傳文件。
下面是後臺servlet接收前端傳來的數據:
public class RegisterInfoServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
HttpSession session = request.getSession();
String ip = Util_GetData.getInboundIp(); //獲取IP
String deviceName = Util_GetData.getDeviceSN();
String resultMsg = "";
String url = "http://"+ip+"/deviceRegister";
DiskFileItemFactory factory = new DiskFileItemFactory();//從這裏開始接受前端的數據 貌似用這個方法需要兩個jar包
// 創建一個文件上傳解析器
ServletFileUpload upload = new ServletFileUpload(factory);
Map<String, String> strParams = new HashMap<>();
String loginName=null;
List<FileItem> list = null;
String filename=null;
InputStream stream=null;
try {
list = upload.parseRequest(request);//獲取到前端所有的數據(即表單裏有name屬性的標籤)
} catch (FileUploadException e) {
e.printStackTrace();
}
for(FileItem item : list) {//遍歷這個list
if (item.isFormField()) {//如果fileitem中封裝的是普通輸入項的數據
String name = item.getFieldName();
String value = item.getString("UTF-8");
strParams.put(name,value);
if(name.equals("loginName")){
loginName = value;
}
continue;
} else {//如果fileitem中封裝的是上傳文件
stream = item.getInputStream();//上傳文件需要的文件流參數 如果只有單個文件,這裏就已經獲取了文件的InputStream
filename = item.getName();//上傳文件需要的參數 這裏獲取了文件名稱
}
}
HttpConnectionUtil cnnUtil = new HttpConnectionUtil();
strParams.put("deviceName",deviceName);//把所有普通數據放入Map
resultMsg = cnnUtil.uploadFile(url,stream,filename,strParams);//調用這個方法訪問另一個服務器,此方法緊跟在下面
if(null != resultMsg && !"".equals(resultMsg) && resultMsg.indexOf("success") > 0){
session.setAttribute("loginName", loginName);
}
PrintWriter pw = response.getWriter();
pw.print(resultMsg);
pw.flush();
pw.close();
}
}
//訪問後臺服務器的方法
package com.util;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.Map;
import java.util.UUID;
/**
* Java原生的API可用於發送HTTP請求,即java.net.URL、java.net.URLConnection,這些API很好用、很常用,
* 但不夠簡便;
*
* 1.通過統一資源定位器(java.net.URL)獲取連接器(java.net.URLConnection)
* 2.設置請求的參數
* 3.發送請求
* 4.以輸入流的形式獲取返回內容 5.關閉輸入流
* @author H__D
*
*/
public class HttpConnectionUtil {
private static final int TIME_OUT = 80 * 1000; //超時時間
private static final String CHARSET = "utf-8"; //編碼格式
private static final String PREFIX = "--"; //前綴
private static final String boundary = UUID.randomUUID().toString(); //邊界標識 隨機生成
private static final String CONTENT_TYPE = "multipart/form-data"; //內容類型
private static final String LINE_END = "\r\n";
/**
* 文件上傳的方法
*
* @param actionUrl:上傳的路徑
* @param filename:文件名稱
* @param stream:上傳文件需要的文件流參數
* @return
*/
public String uploadFile(String actionUrl, InputStream stream,String filename,Map<String, String> strParams) {
DataOutputStream ds = null;
InputStream inputStream = null;
InputStreamReader inputStreamReader = null;
BufferedReader reader = null;
StringBuffer resultBuffer = new StringBuffer();
try {
// 統一資源
URL url = new URL(actionUrl);
// 連接類的父類,抽象類
URLConnection urlConnection = url.openConnection();
// http的連接類
HttpURLConnection httpURLConnection = (HttpURLConnection) urlConnection;
// 設置是否從httpUrlConnection讀入,默認情況下是true;
httpURLConnection.setDoInput(true);
// 設置是否向httpUrlConnection輸出
httpURLConnection.setDoOutput(true);
// Post 請求不能使用緩存
httpURLConnection.setUseCaches(false);
// httpURLConnection.setReadTimeout(TIME_OUT);
// httpURLConnection.setConnectTimeout(TIME_OUT);
// 設定請求的方法,默認是GET
httpURLConnection.setRequestMethod("POST");
// 設置字符編碼連接參數
httpURLConnection.setRequestProperty("Connection", "Keep-Alive");
// 設置字符編碼
httpURLConnection.setRequestProperty("Charset", "UTF-8");
// 設置請求內容類型
httpURLConnection.setRequestProperty("Content-Type", CONTENT_TYPE+";boundary=" + boundary);
// 設置DataOutputStream
ds = new DataOutputStream(httpURLConnection.getOutputStream());
//參數上傳
ds.write(getStrParams(strParams).toString().getBytes());//普通數據上傳完成,這裏有個方法包裝普通數據的Map,在下面
ds.flush();
StringBuilder fileSb = new StringBuilder(); //文件上傳
fileSb.append(PREFIX)
.append(boundary)
.append(LINE_END)
/**
* 這裏重點注意: name裏面的值爲服務端需要的key 只有這個key 纔可以得到對應的文件
* filename是文件的名字,包含後綴名的 比如:abc.png
*/
.append("Content-Disposition: form-data; name=\"file\"; filename=\""
+ filename + "\"" + LINE_END)
.append("Content-Type: image/jpg" + LINE_END) //此處的ContentType不同於 請求頭 中Content-Type
.append("Content-Transfer-Encoding: 8bit" + LINE_END)
.append(LINE_END);// 參數頭設置完以後需要兩個換行,然後纔是參數內容
ds.writeBytes(fileSb.toString());
ds.flush();
byte[] buffer = new byte[1024];
int length;
while ((length = stream.read(buffer)) != -1) {
ds.write(buffer, 0, length);
}
stream.close();
ds.writeBytes(LINE_END);
//請求結束標誌
ds.writeBytes(PREFIX + boundary + PREFIX + LINE_END);
ds.flush();
ds.close(); //文件上傳完成
if (httpURLConnection.getResponseCode() >= 300) {
throw new Exception("請求失敗:" + httpURLConnection.getResponseCode());
}
if (httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
inputStream = httpURLConnection.getInputStream();
inputStreamReader = new InputStreamReader(inputStream);
reader = new BufferedReader(inputStreamReader);
String tempLine = null;
resultBuffer = new StringBuffer();
while ((tempLine = reader.readLine()) != null) {
resultBuffer.append(tempLine);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (ds != null) {
try {
ds.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (inputStreamReader != null) {
try {
inputStreamReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultBuffer.toString();
}
}
private static StringBuilder getStrParams(Map<String,String> strParams){
StringBuilder strSb = new StringBuilder();
for (Map.Entry<String, String> entry : strParams.entrySet() ){
strSb.append(PREFIX)
.append(boundary)
.append(LINE_END)
.append("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"" + LINE_END)
.append("Content-Type: text/plain; charset=" + CHARSET + LINE_END)
.append("Content-Transfer-Encoding: 8bit" + LINE_END)
.append(LINE_END)// 參數頭設置完以後需要兩個換行,然後纔是參數內容
.append(entry.getValue())
.append(LINE_END);
}
return strSb;
}
}
文件上傳很艱難,文件接受更艱難。之前有過很多上傳文件的方式,但苦於不知道後臺該如何接收文件。這次的接收方式也很奇葩。
//設備註冊並實名認證
@RequestMapping(value = "/deviceRegister")
@ResponseBody
public String deviceRegister(HttpServletRequest req, HttpServletResponse res, @RequestParam("file") MultipartFile file, @RequestParam("loginName") String loginName, @RequestParam("loginPwd") String loginPwd, @RequestParam("realName") String realName, @RequestParam("IDCardNum") String IDCardNum, @RequestParam("deviceName") String deviceName){
//數據傳送完成,開始你的表演
}
沒錯,上面接收數據的方式就是在方法體裏面,通過@RequestParam和所有的name,接收對應的數據。
後續還會有各種情況和各種架構下,傳遞+接收文件的博客,哪天心情好就寫咯,反正都是我工作裏面碰到的,第一次開發,又沒有經驗,只能到處百度,到處拼接方法這樣子,唉,說不出的滋味。