所設定的action="URL",將整個頁面request移動到action所指定的地方,例如Servlet,
等處理完後在將User導至其他網頁頁面。
但有沒有可能不要讓User被導到其他頁面,留在原頁面就完成檔案資訊的傳送呢?
答案是可以的,這邊就要利用新的javascript類別FormData和Ajax的技術來達到Ajax傳
送檔案(也可順便傳遞其他input資料)資料。
在這邊我們要利用Netbeans、Servlet3.0、Tomcat 8來實做我們的範例,
實現了三個版本的檔案上傳:Javascript、JQuery、AngularJS
首頁是專案檔案結構,如下圖:
所用的版本JQuery為v2.0.3、AngularJS為v1.5.8,
主要的重要檔案有:
- fileUploadAjaxExample.html
給User上傳檔案的html網頁。 - FileUploadAjax.js
處理檔案資料上傳的Javascript File.
包含Javascript、JQuery、AngularJS三個版本。 - FileUploadAction.java
用來接收檔案資料的servlet,這裡設定URL patter為/fileUpload.do - web.xml
設定檔,其中必須在要接收檔案的servlet中設定的tag, 超過file-size-threshold的檔案request將會以臨時暫存的方式存到硬 碟中,預設為0
tag裡可以有以下的子tag設定:
<location> 檔案存放位置 (使用Part.write(fileName)可以在寫入檔案,但如果fileName
為絕對路徑則以絕對路徑為準)
<max-file-size> 最大檔案size
<max-request-size> 最大request size (例如POST的request size)
<file-size-threshold>
- fileUploadAjaxExample.html
<!DOCTYPE html> <html> <head> <title>File upload ajax example</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="js/libs/angular.js/angular.js"></script> <script src="js/libs/jquery/jquery.js"></script> <script src="js/FileUploadAjax.js"></script> </head> <body> Javascript版: <form id="form_javascript" onsubmit="submitForm_javascript(); return false;" action="#"> <input name="fileDescription" type="text" required/> <input name="fileData" type="file" required/> <input type="submit" /> </form> JQuery版: <form id="form_jquery" onsubmit="submitForm_jquery(); return false;" action="#"> <input name="fileDescription" type="text" required/> <input name="fileData" type="file" required/> <input type="submit" /> </form> AngularJS版: <div ng-app="myApp" ng-controller="myController as myCtrl"> <form ng-submit="myCtrl.submitForm()"> <input name="fileDescription" type="text" ng-model="myCtrl.myFormData.fileDescription" required/> <input name="fileData" type="file" file-uploader ng-model="myCtrl.myFormData.fileData" required/> <input type="submit" /> </form> </div> </body> </html>
可以看到,在fileUploadAjaxExample.html中寫了三個form,並且各自的submit動作都綁到了不同的事件,
Javascript版本的綁定了submitForm_javascript()
JQuery版本的綁定了submitForm_jquery()
AngulrJS版本的綁定了myCtrl.submitForm(),並且還對每一個Input綁定了對應的model,
這裡要注意的是,因為AngularJS並沒有實作intput type="file"的model-view雙向資料綁定,所以我們要實作一個自製的directive, "file-uploader" 來實現由View到model綁定。 - FileUploadAction.java:
//////////Javascript version code - Start ////////// function submitForm_javascript() { var form_javascript = document.getElementById("form_javascript"); var formData = new FormData(form_javascript); var request = new XMLHttpRequest(); request.open("POST", "fileUpload.do", true); request.onload = function (event) { if (request.status == 200) { console.log("OK_javascript"); } else { console.log("Error"); } }; request.send(formData); } //////////Javascript version code - End ////////// ////////// JQuery version code - Start ////////// function submitForm_jquery() { var form_jquery = document.getElementById("form_jquery"); var formData = new FormData(form_jquery); $.ajax({ url: "fileUpload.do", method: "POST", data: formData, processData: false, // 告訴JQuery不要去處理發送的數據,不然會把data //設置的物件轉換成查詢字符串以配合預設的application/x-www-form-urlencoded // contentType: false, // 告訴JQuery不要去設置Content-Type請求Header, //Header會自動適情況加上multipart/form-data success: function (response) { console.log("OK_jquery"); }, error: function (jqXHR, textStatus, errorMessage) { console.log(errorMessage); } }); } //////////JQuery version code - End ////////// //////////AngularJS version code - Start ////////// (function () { var myApp = angular.module("myApp", []); myApp.controller("myController", ["$http", function ($http) { var self = this; self.myFormData = {}; self.submitForm = function () { //將myController.myFormData物件裡的資料都設定 //到Formdata物件中 var formData = getFormDataFormObject(self.myFormData); $http({ url: "fileUpload.do", method: "POST", data: formData, transformRequest: angular.identity, //不對requet資料做處理,angular.identity是一個 //function,會返回第一個傳入參數 (即不對第一個參數 //做處理),例如像是: //function (request){ return request; } headers: {'Content-Type': undefined} //不要去設置Content-Type請求Header, //Header會自動適情況加上multipart/form-data }).success(function (response) { console.log("OK_angularjs"); }).error(function (response) { console.log(response); }); }; }]); //自製的directive,用來處理AngularJS未實作的input type="file" //model-view數據綁定 myApp.directive("fileUploader", ["$parse", function ($parse) { return { restrict: "A", require: 'ngModel', link: function (scope, elm, attrs, ngModel) { elm.bind("change", function () { scope.$apply(function () { if (elm[0].files.length > 0) { //將file資料設定到model上 $parse(attrs.ngModel).assign(scope, elm[0].files[0]); } }); }); } } }]); //將object擁有的屬性全部設定到FormData物件上 function getFormDataFormObject(object) { var formData = new FormData(); Object.getOwnPropertyNames(object).forEach(function (value, index, array) { formData.append(value, object[value]); }); return formData; } })(); //////////AngularJS version code - End //////////
在這邊我們將Javascript、JQuery、AngularJS三個版本的程式碼在同一個檔案中, 用註解分隔以利分別,其本上寫法非常相似,說明都寫在註譯中, 這邊做一些重點說明:- 可以看到我們都沒有在Ajax的請求中設定header,這是為了讓header能自動因資
料而被設定成multipart/form-data。 - 都設定不要對request的data做處理,因為multipart/form-data的編碼方式就是不要
做任何的編碼。 - 因為AngularJS沒有對input type="file"實現model-view的資料綁定,所以在這邊我
們自己實做一個directive,"fileUploader",來處理,
請參考之前的文章:自製檢查file size的directive (input type="file") - AngularJs
- 可以看到我們都沒有在Ajax的請求中設定header,這是為了讓header能自動因資
- FileUploadAction.java
這邊只貼重點部份的Code:protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); request.setCharacterEncoding("UTF-8"); //使用request.getPart()來得到file資料 Part uploadFile = request.getPart("fileData"); //使用Part.getSubmittedFileName()得到上傳的file name String filename = uploadFile.getSubmittedFileName(); //使用Part.getSize()得到上傳的file size int fileSize = (int) uploadFile.getSize(); //使用request.getParameter()來得到input type不等於file的其他參數資料 String fileDescription = request.getParameter("fileDescription"); //使用Part.write()來輸出檔案,可使用相對路徑(參數直接給輸出檔案名稱) //或絕對路徑, //相對路徑會參考web.xml裡<multipart-config> --> <location> 的設置 uploadFile.write(fileDescription + "_" + filename); }
這邊需注意的是Part類別及可以用request.getParameter直接解析multipart/form-data請求等用法是Servlet3.0才有的用法。
源碼下載:
UploadFileAjaxExample.7z
參考資料:
沒有留言 :
張貼留言