HTML5 jQuery+FormData 非同步上傳檔案,帶進度條
阿新 • • 發佈:2019-01-02
利用jQuery和html5的FormData非同步上傳檔案的好處是:
- 實現很簡單
- 很方便地支援進度條
- 很方便地進行擴充套件和美化
先看看效果圖:
圖片上傳後的結果:
實現步驟如下:
第二步:上傳頁面的html程式碼:
[html] view plain copy print?- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- <link href="../resources/css/common.css" rel="stylesheet" />
- <script
- </head>
- <body>
- <h2>HTML5非同步上傳檔案,帶進度條</h2>
- <form method="post" enctype="multipart/form-data">
- 其他需要提交的資訊:<input type="text" name="otherInfo"/><br/><br/>
- 選擇要上傳的檔案:<br/>
- <input
type="file" name="file"
- <input type="file" name="file" /><span></span><br/>
- </form>
- <br/><br/>
- <input type="button" value="上傳吧" onclick="upload()"/>
- <br/><br/>
- 上傳進度:<progress></progress><br/>
- <p
id="progress">
- <p id="info"></p>
- </body>
- </html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link href="../resources/css/common.css" rel="stylesheet" />
<script src="../resources/js/jquery-2.1.4.js"></script>
</head>
<body>
<h2>HTML5非同步上傳檔案,帶進度條</h2>
<form method="post" enctype="multipart/form-data">
其他需要提交的資訊:<input type="text" name="otherInfo"/><br/><br/>
選擇要上傳的檔案:<br/>
<input type="file" name="file" /><span></span><br/>
<input type="file" name="file" /><span></span><br/>
</form>
<br/><br/>
<input type="button" value="上傳吧" onclick="upload()"/>
<br/><br/>
上傳進度:<progress></progress><br/>
<p id="progress">0 bytes</p>
<p id="info"></p>
</body>
</html>
第三步:非同步上傳的JavaScript程式碼(註釋很詳細):
[javascript] view plain copy print?- <script>
- var totalSize = 0;
- //繫結所有type=file的元素的onchange事件的處理函式
- $(':file').change(function() {
- var file = this.files[0]; //假設file標籤沒開啟multiple屬性,那麼只取第一個檔案就行了
- name = file.name;
- size = file.size;
- type = file.type;
- url = window.URL.createObjectURL(file); //獲取本地檔案的url,如果是圖片檔案,可用於預覽圖片
- $(this).next().html("檔名:" + name + " 檔案型別:" + type + " 檔案大小:" + size + " url: " + url);
- totalSize += size;
- $("#info").html("總大小: " + totalSize + "bytes");
- });
- function upload() {
- //建立FormData物件,初始化為form表單中的資料。需要新增其他資料可使用formData.append("property", "value");
- var formData = new FormData($('form')[0]);
- //ajax非同步上傳
- $.ajax({
- url: "http://localhost:8080/MyJavaStudio/servlet/file/upload",
- type: "POST",
- data: formData,
- xhr: function(){ //獲取ajaxSettings中的xhr物件,為它的upload屬性繫結progress事件的處理函式
- myXhr = $.ajaxSettings.xhr();
- if(myXhr.upload){ //檢查upload屬性是否存在
- //繫結progress事件的回撥函式
- myXhr.upload.addEventListener('progress',progressHandlingFunction, false);
- }
- return myXhr; //xhr物件返回給jQuery使用
- },
- success: function(result){
- $("#result").html(result.data);
- },
- contentType: false, //必須false才會自動加上正確的Content-Type
- processData: false //必須false才會避開jQuery對 formdata 的預設處理
- });
- }
- //上傳進度回撥函式:
- function progressHandlingFunction(e) {
- if (e.lengthComputable) {
- $('progress').attr({value : e.loaded, max : e.total}); //更新資料到進度條
- var percent = e.loaded/e.total*100;
- $('#progress').html(e.loaded + "/" + e.total+" bytes. " + percent.toFixed(2) + "%");
- }
- }
- </script>
<script>
var totalSize = 0;
//繫結所有type=file的元素的onchange事件的處理函式
$(':file').change(function() {
var file = this.files[0]; //假設file標籤沒開啟multiple屬性,那麼只取第一個檔案就行了
name = file.name;
size = file.size;
type = file.type;
url = window.URL.createObjectURL(file); //獲取本地檔案的url,如果是圖片檔案,可用於預覽圖片
$(this).next().html("檔名:" + name + " 檔案型別:" + type + " 檔案大小:" + size + " url: " + url);
totalSize += size;
$("#info").html("總大小: " + totalSize + "bytes");
});
function upload() {
//建立FormData物件,初始化為form表單中的資料。需要新增其他資料可使用formData.append("property", "value");
var formData = new FormData($('form')[0]);
//ajax非同步上傳
$.ajax({
url: "http://localhost:8080/MyJavaStudio/servlet/file/upload",
type: "POST",
data: formData,
xhr: function(){ //獲取ajaxSettings中的xhr物件,為它的upload屬性繫結progress事件的處理函式
myXhr = $.ajaxSettings.xhr();
if(myXhr.upload){ //檢查upload屬性是否存在
//繫結progress事件的回撥函式
myXhr.upload.addEventListener('progress',progressHandlingFunction, false);
}
return myXhr; //xhr物件返回給jQuery使用
},
success: function(result){
$("#result").html(result.data);
},
contentType: false, //必須false才會自動加上正確的Content-Type
processData: false //必須false才會避開jQuery對 formdata 的預設處理
});
}
//上傳進度回撥函式:
function progressHandlingFunction(e) {
if (e.lengthComputable) {
$('progress').attr({value : e.loaded, max : e.total}); //更新資料到進度條
var percent = e.loaded/e.total*100;
$('#progress').html(e.loaded + "/" + e.total+" bytes. " + percent.toFixed(2) + "%");
}
}
</script>
第四步:SpringMVC寫好接受和保持檔案的Controller方法: [java] view plain copy print?
- /**
- * 檔案上傳
- * @author XuJijun
- *
- */
- @RestController
- @RequestMapping("/servlet/file")
- public class FileUploadController {
- /**
- * 儲存檔案的目錄,放在web目錄、或一個指定的絕對目錄下
- */
- private static final String SAVE_DIR = "uploadFiles";
- /**
- *
- * @param request
- * @param response
- * @param p form表單中,type="text"的input控制元件,內容通過這個引數傳送過來,以input控制元件中的name屬性來區分
- * @return JSON表示的處理結果
- * @throws ServletException
- * @throws IOException
- */
- @RequestMapping("/upload")
- public JsonResult upload(HttpServletRequest request, HttpServletResponse response, @RequestParam Map<String, Object> p)
- throws ServletException, IOException {
- // 獲取 web application的絕對路徑
- String appPath = request.getServletContext().getRealPath("");
- // 構造檔案存放的路徑
- String savePath = appPath + File.separator + SAVE_DIR;
- // 如果檔案存放路徑不存在,則mkdir一個
- File fileSaveDir = new File(savePath);
- if (!fileSaveDir.exists()) {
- fileSaveDir.mkdirs();
- }
- List<String> fileNames = new ArrayList<>();
- //迴圈所有的part,把part中的檔案儲存到硬碟中
- for (Part part : request.getParts()) {
- String fileName = part.getSubmittedFileName();
- //form表單中的每個input,都在一個不同的part中,
- //所以需要判斷通過fileName是否為空,過濾掉其他型別的input(比如type="text"):
- if(!StringUtils.isEmpty(fileName)){
- part.write(savePath + File.separator + fileName);
- fileNames.add(fileName);
- }
- }
- Map<String, Object> resultData = new HashMap<>();
- resultData.put("savePath", savePath);
- resultData.put("files", fileNames);
- return new JsonResult("200", "檔案上傳成功!", resultData);
- }
- /**
- * 從content-disposition頭中獲取原始檔名
- *
- * content-disposition頭的格式如下:
- * form-data; name="dataFile"; filename="PHOTO.JPG"
- *
- * @param part
- * @return
- */
- @SuppressWarnings("unused")
- private String extractFileName(Part part) {
- String contentDisp = part.getHeader("content-disposition");
- String[] items = contentDisp.split(";");
- for (String s : items) {
- if (s.trim().startsWith("filename")) {
- return s.substring(s.indexOf("=") + 2, s.length()-1);
- }
- }
- return "";
- }
- }
/**
* 檔案上傳
* @author XuJijun
*
*/
@RestController
@RequestMapping("/servlet/file")
public class FileUploadController {
/**
* 儲存檔案的目錄,放在web目錄、或一個指定的絕對目錄下
*/
private static final String SAVE_DIR = "uploadFiles";
/**
*
* @param request
* @param response
* @param p form表單中,type="text"的input控制元件,內容通過這個引數傳送過來,以input控制元件中的name屬性來區分
* @return JSON表示的處理結果
* @throws ServletException
* @throws IOException
*/
@RequestMapping("/upload")
public JsonResult upload(HttpServletRequest request, HttpServletResponse response, @RequestParam Map<String, Object> p)
throws ServletException, IOException {
// 獲取 web application的絕對路徑
String appPath = request.getServletContext().getRealPath("");
// 構造檔案存放的路徑
String savePath = appPath + File.separator + SAVE_DIR;
// 如果檔案存放路徑不存在,則mkdir一個
File fileSaveDir = new File(savePath);
if (!fileSaveDir.exists()) {
fileSaveDir.mkdirs();
}
List<String> fileNames = new ArrayList<>();
//迴圈所有的part,把part中的檔案儲存到硬碟中
for (Part part : request.getParts()) {
String fileName = part.getSubmittedFileName();
//form表單中的每個input,都在一個不同的part中,
//所以需要判斷通過fileName是否為空,過濾掉其他型別的input(比如type="text"):
if(!StringUtils.isEmpty(fileName)){
part.write(savePath + File.separator + fileName);
fileNames.add(fileName);
}
}
Map<String, Object> resultData = new HashMap<>();
resultData.put("savePath", savePath);
resultData.put("files", fileNames);
return new JsonResult("200", "檔案上傳成功!", resultData);
}
/**
* 從content-disposition頭中獲取原始檔名
*
* content-disposition頭的格式如下:
* form-data; name="dataFile"; filename="PHOTO.JPG"
*
* @param part
* @return
*/
@SuppressWarnings("unused")
private String extractFileName(Part part) {
String contentDisp = part.getHeader("content-disposition");
String[] items = contentDisp.split(";");
for (String s : items) {
if (s.trim().startsWith("filename")) {
return s.substring(s.indexOf("=") + 2, s.length()-1);
}
}
return "";
}
}
最後那個私有方法可以不用的,只是為了演示如何直接獲取request header中的資料。
最後,驗證上傳過程中的網路訊息:上傳的訊息頭和資料:
可見,對於表單中的3個input,http request payload中對應有3個part來上傳資料。
Controller處理後的返回結果(JSON格式):
總結:程式碼很簡單,結果很友好。感謝HTML5和SpringMVC!