SpringMVC Ajax上傳檔案例項
做了一個檔案上傳模組,因為傳統的form提交會有頁面重新整理,不符合我的使用要求,所以我採用Ajax提交方式,這裡說明下,我的應用程式前端為Ajax提交,後端SpringMVC接收處理。
傳統form提交檔案方式:
<form id="uploadPic" action="/user/saveHeaderPic" method="post" enctype="multipart/form-data"> <input type="file" name="file"/> <input type="submit" value="提交"/> </form>
採用Ajax提交檔案,我先後出現瞭如下兩個問題:
Ajax post 400 (Bad Request)
HTTP Status 400 - Required CommonsMultipartFile parameter ‘pic’ is not present
這裡先解釋下錯誤原因:
問題1:
Ajax引數錯誤導致,上傳檔案的form我使用了jquery.form.js的form序列化,這樣傳輸表單到後臺很方便,但是二進位制檔案是無法用form.serialize()序列化的。
所以最終我採用了FormData的傳輸方式,XMLHttpRequest Level 2添加了一個新的介面FormData。利用FormData物件,我們可以通過JavaScript用一些鍵值對來模擬一系列表單控制元件,我們還可以使用XMLHttpRequest的send()方法來非同步的提交這個“表單”。比起普通的ajax,使用FormData的最大優點就是我們可以非同步上傳一個二進位制檔案。
但使用formData對瀏覽器有一定要求(Chrome 7+、Firefox 4+、IE 10+、Opera 12+、Safari 5+),如果程式需要相容低版本瀏覽器,可去檢視其他JS上傳控制元件或flash上傳控制元件。
formData使用參看:https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/FormData
問題2:
這個問題是因為不細心導致的 - -,後端接收的引數名和前端file控制元件name名稱不一致導致。
好啦,下面給出我的前後端程式碼示例:
HTML:
<form id="uploadPic" action="#" enctype="multipart/form-data"> <input type="file" name="file"> <a href="javascript:savePic();" class="btn green"> 提交 </a> </form>
JS指令碼:
<script type="text/javascript">
function savePic(){
var formData = new FormData($( "#uploadPic" )[0]);
var ajaxUrl = "${path}/rest/user/saveHeaderPic";
//alert(ajaxUrl);
//$('#uploadPic').serialize() 無法序列化二進位制檔案,這裡採用formData上傳
//需要瀏覽器支援:Chrome 7+、Firefox 4+、IE 10+、Opera 12+、Safari 5+。
$.ajax({
type: "POST",
//dataType: "text",
url: ajaxUrl,
data: formData,
async: false,
cache: false,
contentType: false,
processData: false,
success: function (data) {
alert(data);
},
error: function(data) {
alert("error:"+data.responseText);
}
});
return false;
}
JAVA後端:
/**
* 頭像圖片上傳
* @throws IOException
*/
@RequestMapping(value = "/saveHeaderPic", method = RequestMethod.POST)
public void saveHeaderPic(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request, HttpServletResponse response) throws IOException {
String resMsg = "";
try {
long startTime=System.currentTimeMillis();
System.out.println("fileName:"+file.getOriginalFilename());
String path="/Users/loukai/easylife/files/"+new Date().getTime()+file.getOriginalFilename();
System.out.println("path:" + path);
File newFile=new File(path);
//通過CommonsMultipartFile的方法直接寫檔案
file.transferTo(newFile);
long endTime=System.currentTimeMillis();
System.out.println("執行時間:"+String.valueOf(endTime-startTime)+"ms");
resMsg = "1";
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
resMsg = "0";
}
response.getWriter().write(resMsg);
}
執行測試,檔案上傳成功!
第二部份:
<input type="file" multiple="true" id="file" name="file"/><button class="u-button u-button-primary" onclick="uploadFileMethod1()" id="doUpload">確認附件</button>
js程式碼
單個檔案上傳
function uploadFileMethodSingleFile()
{
var FileController = $ctx+"/fankuiController/saveFiles"; // 接收上傳檔案的後臺地址
// FormData 物件
var fileName = document.getElementById("file").value;
// alert(getPath(fileObj));
var form = new FormData();
var fileObj = document.getElementById("file").files[0]; // 獲取檔案物件
form.append("file ",fileObj ); // 檔案物件
var xhr = new XMLHttpRequest();
xhr.open("post", FileController, true);
xhr.send(form);
xhr.onload = function () {
alert("上傳完成!");
};
}
單個檔案上傳時,controller用MultipartFile 型別的引數接受檔案
@RequestMapping(method = RequestMethod.POST,value = "saveFiles")
public @ResponseBody Map<String,Object> saveFiles(@RequestParam(value = "file") MultipartFile file, HttpServletRequest request) {
Map<String,Object> reg=new HashMap<String,Object>();
//將記憶體中的資料寫入磁碟
System.out.println("開始進行附件上傳");
try {
//String filePath = request.getSession().getServletContext().getRealPath("/");
String filePath ="c:\\";
MultipartFile file = fileArray[i];
String originalFileName = file.getOriginalFilename();
String newFileName = UUID.randomUUID()+originalFileName;
String finalPath = filePath+newFileName;
System.out.println(originalFileName);
System.out.println(newFileName);
System.out.println(finalPath);
System.out.println("引數"+request.getParameter("json_filesNameArray"));
System.out.println("file"+file.getContentType());
File fileAttach = new File(finalPath);
file.transferTo(fileAttach);
} catch (Exception e1) {
e1.printStackTrace();
}
//上傳成功後,將附件加入資料庫附件表的blob欄位中
reg.put("result","success");
return reg;
}
如果是多個檔案上傳 fomdata 中要使用formdata.append() 對多個檔案進行遍歷 然後進行上傳
function uploadFileMethod1()
{
var FileController = $ctx+"/fankuiController/saveFiles"; // 接收上傳檔案的後臺地址
// FormData 物件
var fileName = document.getElementById("file").value;
// alert(getPath(fileObj));
var form = new FormData();
form.append("json_filesNameArray", fileName); // 可以增加表單資料
// var fileArray = new Array();
// for(var i=0;i<fileArray.length;i++)
// {
// var fileObj = document.getElementById("file").files[i]; // 獲取檔案物件
// fileArray.push(fileObj);
// }
var files = document.getElementById("file").files;
for(var i=0; i< files.length; i++){
alert(files[i].name);
form.append("fileArray",files[i]); // 檔案物件
}
// XMLHttpRequest 物件
//var fileObj = document.getElementById("file").files[0];
//form.append("fileArray", fileObj);
var xhr = new XMLHttpRequest();
xhr.open("post", FileController, true);
xhr.send(form);
xhr.onload = function () {
alert("上傳完成!");
};
}
多檔案上傳時,controller端,進行接收時 使用 MultipartFile[] 型別引數 接受 陣列型別的多個檔案,然後遍歷陣列進行操作
@RequestMapping(method = RequestMethod.POST,value = "saveFiles")
public @ResponseBody Map<String,Object> saveFiles(@RequestParam(value = "fileArray") MultipartFile[] fileArray, HttpServletRequest request) {
Map<String,Object> reg=new HashMap<String,Object>();
//將記憶體中的資料寫入磁碟
System.out.println("開始進行附件上傳");
System.out.println(fileArray.length);
try {
for(int i=0;i<fileArray.length;i++)
{
//String filePath = request.getSession().getServletContext().getRealPath("/");
String filePath ="c:\\";
MultipartFile file = fileArray[i];
String originalFileName = file.getOriginalFilename();
String newFileName = UUID.randomUUID()+originalFileName;
String finalPath = filePath+newFileName;
System.out.println(originalFileName);
System.out.println(newFileName);
System.out.println(finalPath);
System.out.println("引數"+request.getParameter("json_filesNameArray"));
System.out.println("file"+file.getContentType());
File fileAttach = new File(finalPath);
file.transferTo(fileAttach);
}
} catch (Exception e1) {
e1.printStackTrace();
}
第三部份:
頁面表單:
<form id="frm_identityA" action="" enctype="multipart/form-data">
<span style="display:none">
<input type="file" id="identityA" name="identityA" value="">
</span>
<input type="hidden" name="mobile" value="***********">
</form>
頁面圖片標籤和按鈕:
<img id="img_identityA" src="app/images/icon011.png" alt="" name="img_identityA"/>
<button type="button" id="button_identityA" class="btn btn-primary btn-lg btn-block">點選上傳圖片</button>
頁面操作:
// 點選按鈕的時候選擇圖片
$("#button_identityA").click(function(){
$("#identityA").click();
});
// input框改變的時候將圖片傳送給後臺
$("#identityA").change(function() {
var formData = new FormData($("#frm_identityA")[0]);
$.ajax({
url : "userregeste/file/upload.do", // 自行按需配置好完整的url,包括ip和埠號
type : "POST",
data : formData,
async : false,
cache : false,
contentType : false,
processData : false,
success : function(returndata) {
alert("success");
$("#img_identityA").attr("src","userregeste/file/showImages.do?mobile=***********&name=identityA&"+new Date().toTimeString());
$("#img_identityA").attr("width","124");
$("#img_identityA").attr("height","124");
},
error : function(returndata) {
alert("error");
$("#img_identityA").attr("src","app/images/icon011.png");
}
});
});
Spring配置:
<!-- 圖片獲取 maxUploadSize:設定最大限制 位元組為單位-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="1024000"></property>
</bean>
<!-- SpringMVC檔案上傳 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--defaultEncoding:請求的編碼格式必須和使用者JSP的編碼一致,以便正確讀取表單中的內容。
uploadTempDir:檔案上傳過程中的臨時目錄,上傳完成後,臨時檔案會自動刪除
maxUploadSize:設定檔案上傳大小上限(單位為位元組) -->
<property name="defaultEncoding" value="UTF-8" />
<property name="maxUploadSize" value="102400000" />
<!-- uploadTempDir可以不做設定,有預設的路徑,上傳完畢會臨時檔案會自動被清理掉 -->
<property name="uploadTempDir" value="upload/temp"></property>
</bean>
1.defaultEncoding:表示用來解析request請求的預設編碼格式,當沒有指定的時候根據Servlet規範會使用預設值 ISO-8859-1 。當request自己指明瞭它的編碼格式的時候就會忽略這裡指定的defaultEncoding。
2.uploadTempDir:設定上傳檔案時的臨時目錄,預設是Servlet容器的臨時目錄。
3.maxUploadSize:設定允許上傳的最大檔案大小,以位元組為單位計算。當設為-1時表示無限制,預設是-1。
4.maxInMemorySize:設定在檔案上傳時允許寫到記憶體中的最大值,以位元組為單位計算,預設是10240。
Action中的程式碼:
1、圖片上傳
@Controller
@RequestMapping("/userregeste")
public class ImageUpLoadAction {
/**
* 存放上傳的圖片資訊
*/
private static Map<String,byte[]> images;
static {
images = new HashMap<String, byte[]>();
}
@RequestMapping("/file/upload")
public ProcessResultModel upLoad(HttpServletRequest request,HttpServletResponse response) {
// 從請求中獲取到檔案資訊需要將請求轉換為MultipartHttpServletRequest型別
MultipartHttpServletRequest MulRequest = request instanceof MultipartHttpServletRequest ? (MultipartHttpServletRequest) request : null;
request.getParameter("mobile");// 依然可以從請求中獲取到除圖片之外的引數
Iterator<String> fileNames = MulRequest.getFileNames();
if (fileNames.hasNext()) { // 遍歷請求中的圖片資訊
String fileName = fileNames.next(); // 圖片對應的引數名
log.debug("fileName:" + fileName);
file = MulRequest.getFile(fileName); // 獲取到圖片
if (file != null) {
log.debug("file.getSize():" + file.getSize()); // 圖片大小
file.getBytes();// 可以獲取到圖片的位元組陣列
images.put(fileName,file.getBytes());// 獲取到圖片以位元組陣列形式儲存在伺服器記憶體中
}
}
}
}
2、圖片顯示
Ajax請求傳送成功之後的方法中的操作,
$("#img_identityA").attr("src","userregeste/file/showImages.do?mobile=***********&name=identityA&"+new Date().toTimeString());
值得一提的是,img標籤中的src屬性,對於UI人員來說就是存放靜態圖片資源的,但是對於程式設計師來說,應該要知道img標籤實際上會根據src屬性去傳送一次請求。同時,瀏覽器對於img的src屬性的處理方式是如果src屬性值不變,只會傳送一次請求,所有加上new Date().toTimeString(),使每次的請求都不相同。
程式碼如下:
@RequestMapping("/file/showImages")
public String showImages(HttpServletRequest request,HttpServletResponse response) throws IOException {
log.debug("請求地址:"+request.getRequestURI()+",開始獲取圖片");
OutputStream sout = null;
String mobile = request.getParameter("mobile");
String name = request.getParameter("name"); // 圖片名稱
log.debug("mobile:"+mobile+" name:"+name);
if (mobile == null || name == null) {
log.debug("手機號或圖片名為空,獲取圖片失敗!");
return null;
}
byte[] pictrue = null;
// 從本地Map中去獲取images圖片
pictrue = images.get(name);
log.debug("圖片大小:"+pictrue.length);
try {
if (pictrue != null) {
response.setContentType("text/html");
sout = response.getOutputStream();
sout.write(pictrue);
sout.flush();
sout.close();
sout = null;
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (sout != null) {
sout.close();
sout = null;
}
}
log.debug("返回圖片成功!");
return null;
}
如此,頁面上會成功的顯示圖片。
*需要的jar包除了SpringMVC的所有jar包之外,還需要
commons-fileupload-1.3.1.jar
commons-io.jar
*設定enctype="multipart/form-data"後,表單資料是以二進位制形式進行傳輸的,commons-fileupload-1.3.1.jar 即是幫我們解析二進位制資料並且封裝到parameter裡面,不新增這個包,從HttpServletRequest獲取不到引數,可以獲取二進位制流資料自行解析。
*以上的實現動態的將圖片傳送到後臺,可以在後臺對圖片進行一系列處理,效率比較高。
頁面效果: