文件上傳的原理: 表單元素的enctype屬性指定的是表單數(shù)據(jù)的編碼方式,該屬性有3個值: 1) application/x-www-form-urlencoded:這是默認編碼方式,它只處理表單域里的value屬性值,采用這種編碼方式的表單會將表單域的 值處理成URL編碼方式。 2) multipart/form-data:這種編碼方式的表單會以二進制流的方式來處理表單數(shù)據(jù),這種編碼方式會把文件域指定文件的內(nèi)容也封裝到 請求參數(shù)里。 3) text/plain:這種方式主要適用于直接通過表單發(fā)送郵件的方式。 文件上傳是web應用經(jīng)常用到的一個知識。原理是,通過為表單元素設(shè)置enctype=”multipart/form-data”屬性,讓表單提交的數(shù)據(jù)以二 進制編碼的方式提交,在接收此請求的Servlet中用二進制流來獲取內(nèi)容,就可以取得上傳文件的內(nèi)容,從而實現(xiàn)文件的上傳。 在Java領(lǐng)域中,有兩個常用的文件上傳項目:一個是Apache組織Jakarta的Common-FileUpload組件 ([url]http://commons./fileupload/[/url]),另一個是Oreilly組織的COS框架([url]http://www./cos/[/url])。利用這兩個框架都能很方便 的實現(xiàn)文件的上傳。 ================================== Struts2上傳文件 增加commons-fileupload-1.2.jar和commons-io-1.3.1.jar到lib ===== jsp ===== form 的 enctype 設(shè)置為 multipart/form-data ============== UploadAction ============== private String username; private String password; private File file; // 對應文件域 private String fileFileName; // 前面的File屬性的名字 + FileName(固定的) private String fileContent; // 前面的File屬性的名字 + Content // setter... getter... String execute() throws Exception { InputStream is = new FileInputStream( file ); String root = ServletActionContext.getRequest().getRealPath("/upload"); File destFile = new File(root,this.getFileFileName()); OutputStream os = new FileOutputStream( destFile ); byte[] buffer = new byte[400]; int length = 0; while( (length = is.read(buffer)) > 0 ) { os.write(buffer,0,length); } is.close(); os.close(); return SUCCESS; } ================= 中文問題 ================= 不過我全部用UTF-8并未遇到中文問題 struts2-core包 struts-default.xml ----攔截器的設(shè)置 org.apache.struts2.default.properties ----全局屬性的設(shè)置 33行 strusts.i18n.encoding=UTF-8 默認UTF-8 可以在struts.xml下進行設(shè)置 <struts> 設(shè)置字符集 <constant name="struts.i18n.encoding" value="gbk"/> 設(shè)置上傳文件緩存 <constant name="struts.multipart.saveDir" value="c:\"/> </struts> 其他屬性 struts.multipart.parser=jakarta struts2采用那種方式上傳 pell cos struts.multipart.maxSize=2097152 默認上傳文件最大的請求大小2M struts.action.extension=action 整個url的后綴名 ================ 上傳多個文件 ================ 有兩種方式: 1.數(shù)組 File[] file 文件 String[] fileFileName 文件名 String[] fileContentType 文件類型 2.集合 List<File> file List<String> fileFileName List<String> fileContentType -------- action中: -------- String execute() { for(int i = 0; i < file.size(); i++) { InputStream is = new FileInputStream(file.get(i)); String root = ServletActionContext.getRequest().getRealPath("/upload"); File destFile = new File(root,this.getFileFileName().get(i)); ... } return SUCCESS; } ------ jsp中: ------ 多個file時,file的名字要一致,都要叫file,它會自動set到跟file名對應的List中去 <s:file name="file" /> <s:file name="file" /> <s:file name="file" /> ======================== 上傳任意個文件 ======================== <td id="more"> <input type="button" value="添加" /> </td> ------ JS: ------ funcation addMore() { var td = document.getElementById("more"); //生成一個換行符 var br = document.createElement("br"); //創(chuàng)建一個input組件 var input = document.createElement("input"); var button = document.createElement("input"); //指定類型 為 file 為文件上傳 input.type = "file"; //指定組件的名字 input.name = "file"; button.type = "button"; button.value = "刪除"; //為刪除按鈕注冊一個事件 button.onclick = function() { //alert("刪除按鈕"); //刪除一行 td.removeChild(br); td.removeChild(input); td.removeChild(button); } //將創(chuàng)建的組件加到<td>中 td.appendChild(br); td.appendChild(input); td.appendChild(button); } ======================= 限制上傳類型 ======================= org.apache.struts2.interceptor.FileUploadInterceptor類 Long maximumSize:最大上傳大小---每一個文件的大小,不是總和 String allowedTypes:允許的類型 ------------- struts.xml ------------- <struts> <action ...> <result name="input">/upload.jsp</result> <result .../> 加入一個上傳文件的攔截器并設(shè)置其屬性 <interceptor-ref name="fileUpload"> <param name="maximumSize">409600</param> 單個上傳文件最大不能超過400K <param name="allowedTypes">...</param> mime類型,多個用逗號分開 </interceptor-ref> ** 加入默認的攔截器 <interceptor-ref name="defaultStack" /> </action> </struts> 注:后綴可以到tomcat\conf\web.xml中找<mime-type>中的字符串 -------------- upload.jsp -------------- 添加<s:fielderror /> ---------------------- 更改顯示的錯誤信息 ---------------------- org.apache.struts2中 找到struts-messages.properties ----------------------- 上傳文件類型不匹配 struts.messages.error.content.type.not.allowed=Content-Type not allowed: {0} "{1}" {2} ----------------------- 上傳文件大小超出規(guī)定 struts.messages.error.file.too.large=File too large: {0} "{1}" {2} ----------------------- 上傳文件出錯 struts.messages.error.uploading=Error uploading: {0} 創(chuàng)建一個全局的屬性文件 /src/messages.properties struts.messages.error.content.type.not.allowed=不支持上傳該類型的文件 struts.messages.error.file.too.large=上傳文件過大,請重試 struts.messages.error.uploading=上傳文件時發(fā)生錯誤 --------- 國際化 --------- <constant name="struts.custom.i18n.resources" value="messages"/> messages_en_US.properties messages_zh_CN.properties ============================== 下載 ============================== 處理下載的類:org.apache.struts2.dispatcher.StreamResult == 屬性 == String contentType = "text/plain"; String contentLength; String contentDisposition = "inline"; String inputName = "inputStream"; InputStream inputStream; int bufferSize = 1024; == 說明 == contentType 內(nèi)容類型,和互聯(lián)網(wǎng)MIME標準中的規(guī)定類型一致,例如text/plain代表純文本,text/xml表示XML,image/gif代表GIF圖片,image/jpeg代表JPG圖片 用來做動態(tài)文件下載的,事先并不知道未來的文件類型是什么,那么我們可以把它的值設(shè)置成為:application/octet-stream;charset=ISO8859-1 ,注意一定要加入charset,否則某些時候會導致下載的文件出錯 inputName 下載文件的來源流,對應著action類中某個類型為Inputstream的屬性名,例如取值為inputStream的屬性需要編寫getInputStream()方法 contentDisposition 文件下載的處理方式,包括內(nèi)聯(lián)(inline)和附件(attachment)兩種方式,而附件方式會彈出文件保存對話框,否則瀏覽器會嘗試直接顯示文件。取值為:attachment;filename="struts2.txt",表示文件下載的時候保存的名字應為struts2.txt。如果直接寫filename="struts2.txt",那么默認情況是代表inline,瀏覽器會嘗試自動打開它,等價于這樣的寫法:inline; filename="struts2.txt" bufferSize 下載緩沖區(qū)的大小 # contentType屬性和contentDisposition分別對應著HTTP響應中的頭Content-Type和Content-disposition頭。 如: HTTP頭內(nèi)容: HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-disposition: attachment;filename="struts2.txt" Content-Type: text/plain Transfer-Encoding: chunked Date: Sun, 02 Mar 2008 02:58:25 GMT ---------- action ---------- Class DownloadAction extends ActionSupport { private String path; // setter... getter... //必須返回一個輸入流,該流是讓用戶下載的 public InputStream getDownloadFile() { //從某個文件獲得流 --這里是獲得項目root下upload下的文件 //也可以 new FileInputStream("c:/test.text"); return ServletActionContext.getServletContext().getResourceAsStream("/upload/struts2.ppt"); } public String execute() throws Exception { return SUCCESS; } } ----------- struts.xml ----------- <action name="download" class="org.scorpio.jh.struts2.upload.action.DownloadAction"> <!-- 依賴注入文件路徑 --> <param name="path">/download/xhtml.txt</param> <!-- 設(shè)置結(jié)果類型為 流 --> <result name="success" type="stream"> <!-- 設(shè)置內(nèi)容類型 --> <param name="contentType">text/plain</param> <!-- 設(shè)置下載文件的名字 attachment:作為附件,filename=:指定下載文件名--> <param name="contentDisposition">attachment;filename="xhtml.txt"</param> <!-- 設(shè)置下載文件的輸入流對應的方法 downloadFile對應DownloadAction中的getDownloadFile()--> <param name="inputName">downloadFile</param> <!-- 指定下載文件的緩沖大小 --> <param name="bufferSize">4096</param> </result> </action> ========================== 解決下載文件名中文問題 ========================== 1.在下載action獲取文件名的方法中先進行轉(zhuǎn)碼然后再返回 path = new String( path.getBytes(), "ISO-8859-1" ); 2.xml配置文件動態(tài)的獲取path的值 <param name="contentDisposition">attachment;filename="${path}"</param> ${path} 用于動態(tài)的獲取所配置的action中path成員的值,相當于調(diào)用getPath()方法 ------- action ------- private String path; public String getPath() { try { //轉(zhuǎn)換成西歐字符集 path = new String( path.getBytes(), "ISO-8859-1" ); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return path; } public void setPath(String path) { this.path = path; } --------------- struts.xml --------------- <action name="download" class="org.scorpio.jh.struts2.upload.action.DownloadAction"> <param name="path">/download/wmlscript實例.txt</param> <result name="success" type="stream"> <param name="contentType">text/plain</param> <!-- 動態(tài)的獲取 DownloadAction的path屬性 --> <param name="contentDisposition">attachment;filename="${path}"</param> <param name="inputName">downloadFile</param> <param name="bufferSize">4096</param> </result> </action> ================= 安全隱患 ================= 訪問者如果精通Struts 2的話,它可能使用這樣的帶有表單參數(shù)的地址來訪問: [url]http://localhost:8080/struts2hello/download3.action?inputPath=/WEB-INF/web.xml[/url],這樣的結(jié)果就是下載后的文件內(nèi)容是您系統(tǒng)里面的web.xml的文件的源代碼,甚至還可以用這種方式來下載任何其它JSP文件的源碼。這對系統(tǒng)安全是個很大的威脅。作為一種變通的方法,讀者最好是從數(shù)據(jù)庫中進行路徑配置,然后把Action類中的設(shè)置inputPath的方法統(tǒng)統(tǒng)去掉,簡言之就是刪除這個方法定義: public void setPath(String path) { this.path = path; } 而實際情況則應該成為 download.action?fileid=1 類似于這樣的形式來進行?;蛘吣兀x者可以在execute()方法中進行路徑檢查,如果發(fā)現(xiàn)有訪問不屬于download下面文件的代碼,就一律拒絕,不給他們返回文件內(nèi)容。例如,我們可以把剛才類中的execute()方法加以改進,成為這樣: public String execute() throws Exception { // 文件下載目錄路徑 String downloadDir = ServletActionContext.getServletContext().getRealPath("/download"); // 文件下載路徑 String downloadFile = ServletActionContext.getServletContext().getRealPath(inputPath); java.io.File file = new java.io.File(downloadFile); downloadFile = file.getCanonicalPath();// 真實文件路徑,去掉里面的..等信息 // 發(fā)現(xiàn)企圖下載不在 /download 下的文件, 就顯示空內(nèi)容 if(!downloadFile.startsWith(downloadDir)) { return null; } return SUCCESS; } 這時候如果訪問者再企圖下載web.xml的內(nèi)容,它只能得到一個空白頁,現(xiàn)在訪問者只能下載位于/download目錄下的文件
|
|