文件上傳一個經(jīng)常用到的功能,它有許多中實現(xiàn)的方案。
頁面表單 + RFC1897規(guī)范 + http協(xié)議上傳
頁面控件(flash/html5/activeX/applet) + RFC1897規(guī)范 + http協(xié)議上傳
頁面控件(flash/html5/activeX/applet) + 自定義數(shù)據(jù)規(guī)范 + http協(xié)議上傳
頁面控件(flash/html5/activeX/applet) + FTP協(xié)議上傳
頁面控件(flash/html5/activeX/applet) + 自定義協(xié)議
?
? 用apache common upload組件實際就是采用的“頁面表單 + RFC1897規(guī)范 + http協(xié)議上傳”實現(xiàn)方式,需要實現(xiàn)的技術(shù)點:
1. 多文件數(shù)據(jù)的提交
2. 文件數(shù)據(jù)包接收存儲功能
3. 文件數(shù)據(jù)上傳進(jìn)度
4.?WEB頁面無刷新異步提交
?
?時序圖:
實現(xiàn)思路:
1. 多文件數(shù)據(jù)的提交
在WEB頁面采用多個<input type="file">利用form表單進(jìn)行文件提交
?
2. 文件數(shù)據(jù)包接收存儲功能
服務(wù)端采用servlet,利用apache common upload組件接收解析數(shù)據(jù)包,接收解析的過程中保存進(jìn)度到session, 文件接收完畢后保存到指定目錄
?
3. 文件數(shù)據(jù)上傳進(jìn)度
在WEB頁面在界面寫一個定時器,定時訪問服務(wù)器提供上傳進(jìn)度獲取功能的servlet,獲取文件上傳進(jìn)度信息
?
4. WEB頁面無刷新異步提交
利用iframe來實現(xiàn)WEB頁面無刷新異步上傳
?
關(guān)鍵代碼:
UploadFileServlet.java
Java代碼?
?
package?com.test.servlet;????import?java.io.File;??import?java.io.IOException;??import?java.io.Writer;??import?java.util.Iterator;??import?java.util.List;????import?javax.servlet.ServletException;??import?javax.servlet.http.HttpServlet;??import?javax.servlet.http.HttpServletRequest;??import?javax.servlet.http.HttpServletResponse;????import?org.apache.commons.fileupload.FileItem;??import?org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException;??import?org.apache.commons.fileupload.disk.DiskFileItemFactory;??import?org.apache.commons.fileupload.servlet.FileCleanerCleanup;??import?org.apache.commons.fileupload.servlet.ServletFileUpload;??import?org.apache.commons.io.FileCleaningTracker;??import?org.apache.commons.io.FileUtils;??import?org.apache.commons.io.FilenameUtils;??import?org.apache.commons.io.IOUtils;??import?org.apache.commons.lang3.ArrayUtils;??import?org.apache.commons.logging.Log;??import?org.apache.commons.logging.LogFactory;????/**??*?文件上傳數(shù)據(jù)接收類??*???*?@author?chengqi??*??*/??public?class?UploadFileServlet?extends?HttpServlet?{????????/**?日志對象*/??????private?Log?logger?=?LogFactory.getLog(this.getClass());????????private?static?final?long?serialVersionUID?=?1L;????????/**?上傳目錄名*/??????private?static?final?String?uploadFolderName?=?"uploadFiles";????????/**?上傳臨時文件存儲目錄*/??????private?static?final?String?tempFolderName?=?"tempFiles";????????/**?上傳文件最大為30M*/???????private?static?final?Long?fileMaxSize?=?30000000L;?????????/**?允許上傳的擴(kuò)展名*/??????private?static?final?String?[]?extensionPermit?=?{"txt",?"xls",?"zip"};????????/**?統(tǒng)一的編碼格式*/??????private?static?final?String?encode?=?"UTF-8";????????@Override??????protected?void?doPost(HttpServletRequest?request,?HttpServletResponse?response)?throws?ServletException,?IOException?{??????????logger.info("UploadFileServlet#doPost()?start");??????????try?{??????????????String?curProjectPath?=?this.getServletContext().getRealPath("/");??????????????String?saveDirectoryPath?=?curProjectPath?+?"/"?+?uploadFolderName;??????????????String?tempDirectoryPath?=?curProjectPath?+?"/"?+?tempFolderName;??????????????File?saveDirectory?=?new?File(saveDirectoryPath);??????????????File?tempDirectory?=?new?File(tempDirectoryPath);??????????????logger.debug("Project?real?path?["?+?saveDirectory.getAbsolutePath()?+?"]");??????????????//上傳時產(chǎn)生的臨時文件的默認(rèn)保存目錄??????????????logger.debug("Temp?files?default?save?path?["?+?System.getProperty("java.io.tmpdir")?+?"]");??????????????DiskFileItemFactory?factory?=?new?DiskFileItemFactory();??????????????//DiskFileItemFactory中DEFAULT_SIZE_THRESHOLD=10240表示如果上傳文件大于10K則會產(chǎn)生上傳臨時文件??????????????//上傳臨時文件的默認(rèn)目錄為java.io.tmpdir中保存的路徑,根據(jù)操作系統(tǒng)的不同會有區(qū)別????????????????????????????if(!tempDirectory.exists())?{??????????????????tempDirectory.mkdir();??????????????}??????????????//重新設(shè)置臨時文件保存目錄??????????????factory.setRepository(tempDirectory);????????????????//設(shè)置文件清除追蹤器,文件上傳過程中產(chǎn)生的臨時文件會在??????????????FileCleaningTracker?fileCleaningTracker?=?FileCleanerCleanup.getFileCleaningTracker(this.getServletContext());??????????????factory.setFileCleaningTracker(fileCleaningTracker);????????????????ServletFileUpload?upload?=?new?ServletFileUpload(factory);????????????????//設(shè)置文件上傳進(jìn)度監(jiān)聽器??????????????FileProcessListener?processListener?=?new?FileProcessListener(request.getSession());??????????????upload.setProgressListener(processListener);????????????????//?設(shè)置文件上傳的大小限制??????????????upload.setFileSizeMax(fileMaxSize);????????????????//?設(shè)置文件上傳的頭編碼,如果需要正確接收中文文件路徑或者文件名??????????????//?這里需要設(shè)置對應(yīng)的字符編碼,為了通用這里設(shè)置為UTF-8??????????????upload.setHeaderEncoding(encode);????????????????//解析請求數(shù)據(jù)包??????????????List<FileItem>?fileItems?=?upload.parseRequest(request);??????????????//遍歷解析完成后的Form數(shù)據(jù)和上傳文件數(shù)據(jù)??????????????for?(Iterator<FileItem>?iterator?=?fileItems.iterator();?iterator.hasNext();)?{??????????????????FileItem?fileItem?=?iterator.next();??????????????????String?fieldName?=?fileItem.getFieldName();??????????????????String?name?=?fileItem.getName();??????????????????//如果為上傳文件數(shù)據(jù)??????????????????if(!fileItem.isFormField())?{??????????????????????logger.debug("fieldName["?+?fieldName?+?"]?fileName["?+?name?+?"]?");??????????????????????if(fileItem.getSize()?>?0)?{??????????????????????????String?fileExtension?=?FilenameUtils.getExtension(name);??????????????????????????if(!ArrayUtils.contains(extensionPermit,?fileExtension))?{??????????????????????????????throw?new?NoSupportExtensionException("No?Support?extension.");??????????????????????????}??????????????????????????String?fileName?=?FilenameUtils.getName(name);??????????????????????????FileUtils.copyInputStreamToFile(fileItem.getInputStream(),???????????????????????????????????new?File(saveDirectory,?fileName));??????????????????????}??????????????????}?else?{?//Form表單數(shù)據(jù)??????????????????????String?value?=?fileItem.getString(encode);??????????????????????logger.debug("fieldName["?+?value?+?"]?fieldValue["?+?fieldName?+?"]");??????????????????}??????????????}??????????????responseMessage(response,?State.OK);??????????}?catch(FileSizeLimitExceededException?e)?{???????????????logger.error(e.getMessage(),?e);??????????????responseMessage(response,?State.OVER_FILE_LIMIT);??????????}?catch(NoSupportExtensionException?e)?{???????????????logger.error(e.getMessage(),?e);??????????????responseMessage(response,?State.NO_SUPPORT_EXTENSION);??????????}?catch(Exception?e)?{??????????????logger.error(e.getMessage(),?e);??????????????responseMessage(response,?State.ERROR);??????????}?finally?{??????????????//清除上傳進(jìn)度信息??????????????request.getSession().removeAttribute("fileUploadProcess");??????????}??????????logger.info("UploadFileServlet#doPost()?end");???????}????????public?enum?State?{??????????OK(200,?"上傳成功"),??????????ERROR(500,?"上傳失敗"),??????????OVER_FILE_LIMIT(501,?"超過上傳大小限制"),??????????NO_SUPPORT_EXTENSION(502,?"不支持的擴(kuò)展名");????????????private?int?code;??????????private?String?message;??????????private?State(int?code,?String?message)?{??????????????this.code?=?code;??????????????this.message?=?message;??????????}????????????public?int?getCode()?{??????????????return?code;??????????}??????????public?String?getMessage()?{??????????????return?message;??????????}????????}????????/**??????*?返回結(jié)果函數(shù)??????*?@param?response??????*?@param?state??????*/??????private?void?responseMessage(HttpServletResponse?response,?State?state)?{??????????response.setCharacterEncoding(encode);??????????response.setContentType("text/html;?charset="?+?encode);??????????Writer?writer?=?null;??????????try?{??????????????writer?=?response.getWriter();??????????????writer.write("<script>");??????????????writer.write("window.parent.fileUploadCallBack({\"code\":"?+?state.getCode()?+",\"message\":\""?+?state.getMessage()+?"\"});");??????????????writer.write("</script>");??????????????writer.flush();??????????????writer.close();??????????}?catch(Exception?e)?{??????????????logger.error(e.getMessage(),?e);??????????}?finally?{??????????????IOUtils.closeQuietly(writer);??????????}??????}??????}??
??
?
GetFileProcessServlet.java
Java代碼?
?
package?com.test.servlet;????import?java.io.IOException;??import?java.io.Writer;????import?javax.servlet.ServletException;??import?javax.servlet.http.HttpServlet;??import?javax.servlet.http.HttpServletRequest;??import?javax.servlet.http.HttpServletResponse;????import?org.apache.commons.io.IOUtils;??import?org.apache.commons.logging.Log;??import?org.apache.commons.logging.LogFactory;????/**??*?文件上傳進(jìn)度獲取Servlet??*???*?@author?chengqi??*??*/??public?class?GetFileProcessServlet?extends?HttpServlet?{????????/**?日志對象*/??????private?Log?logger?=?LogFactory.getLog(this.getClass());????????private?static?final?long?serialVersionUID?=?1L;????????@Override??????protected?void?doGet(HttpServletRequest?request,?HttpServletResponse?response)??????????????throws?ServletException,?IOException?{??????????logger.info("GetFileProcessServlet#doGet?start");??????????String?fileUploadPercent?=?(String)request.getSession().getAttribute("fileUploadProcess");??????????Writer?writer?=?null;??????????try?{??????????????writer?=?response.getWriter();??????????????logger.info("percent:"?+?fileUploadPercent);??????????????IOUtils.write(fileUploadPercent?==?null???"0%"?:?fileUploadPercent,?writer);??????????????writer.flush();??????????????writer.close();??????????}?catch(Exception?e)?{??????????????logger.error(e.getMessage(),?e);??????????}?finally?{??????????????IOUtils.closeQuietly(writer);??????????}??????????logger.info("GetFileProcessServlet#doGet?end");??????}????}??
?
FileProcessListener.java
Java代碼?
?
package?com.test.servlet;????import?java.text.NumberFormat;????import?javax.servlet.http.HttpSession;????import?org.apache.commons.fileupload.ProgressListener;??import?org.apache.commons.logging.Log;??import?org.apache.commons.logging.LogFactory;????/**??*?文件進(jìn)度監(jiān)聽器??*???*?@author?chengqi??*??*/??public?class?FileProcessListener?implements?ProgressListener{????????/**?日志對象*/??????private?Log?logger?=?LogFactory.getLog(this.getClass());????????private?HttpSession?session;????????public?FileProcessListener(HttpSession?session)?{??????????this.session?=?session;????????}??????????????public?void?update(long?pBytesRead,?long?pContentLength,?int?pItems)?{??????????double?readByte?=?pBytesRead;??????????double?totalSize?=?pContentLength;??????????if(pContentLength?==?-1)?{??????????????logger.debug("item?index["?+?pItems?+?"]?"?+?pBytesRead?+?"?bytes?have?been?read.");??????????}?else?{??????????????logger.debug("item?index["?+?pItems?+?"]?"?+?pBytesRead?+?"?of?"?+?pContentLength?+?"?bytes?have?been?read.");??????????????String?p?=?NumberFormat.getPercentInstance().format(readByte?/?totalSize);??????????????session.setAttribute("fileUploadProcess",?p);??????????}??????}????}??
?
?
apacheUploadDemo.html
?
Html代碼?
?
<!DOCTYPE?html?PUBLIC?"-//W3C//DTD?HTML?4.01?Transitional//EN"?"http://www.w3.org/TR/html4/loose.dtd">??<html>??<head>??????<title>Apache?common實現(xiàn)基本文件上傳</title>??????<meta?http-equiv="Content-Type"?content="text/html;?charset=UTF-8">??????<script?type="text/javascript"?src="js/jquery/jquery-1.9.1.js"></script>??????<script?type="text/javascript"?src="js/jquery/jquery.form.js"></script>??????<script?type="text/javascript">????????//定時器對象??????var?uploadProcessTimer?=?null;????????$(function?(){??????????//綁定定時器開始操作到提交按鈕??????????$('input[type=submit]').click(function?()?{??????????????//啟動上傳進(jìn)度查詢定時器??????????????uploadProcessTimer?=?window.setInterval(getFileUploadProcess,?20);??????????})??????});????????//獲取文件上傳進(jìn)度??????function?getFileUploadProcess()?{??????????$.get('/upload/getFileProcessServlet',?function(data)?{??????????????$('#fileUploadProcess').html(data);??????????});??????}????????//上傳完成后,由iframe返回腳本自動調(diào)用??????function?fileUploadCallBack(res)?{??????????//清除定時器??????????if(uploadProcessTimer)?{??????????????window.clearInterval(uploadProcessTimer);??????????}??????????var?message?=?res['message'];??????????var?code?=?res['code'];??????????if(code?!=?200)?{??????????????$('#fileUploadProcess').html('0%');??????????}??????????alert(message);??????}????????</script>??</head>??<body>??<h2>上傳文件1</h2>????用戶信息:??<br/>??<form?id="testForm"?action="/upload/uploadServlet"?method="post"?enctype="multipart/form-data"?target="iframeUpload">??????姓名:<input?name="name"?type="text">?<br/>??????附件1:<input?name="file1"?type="file"?>?<br/>??????附件2:<input?name="file2"?type="file"?>?<br/>??????<br><br>??????<input?type="submit"?value="提交"?><br/>??</form>??上傳進(jìn)度:<label?id="fileUploadProcess"></label>??<iframe?name="iframeUpload"?src=""?width="350"?height="35"?frameborder=0??SCROLLING="no"?style="display:NONE"></iframe>?????</body>??</html>??
?
總結(jié):
雖然使用apache common upload組件實現(xiàn)了文件上傳,但是從上傳的效果來看,并不是一個很完美的解決方案。
有如下缺點:
1. 當(dāng)有多個文件上傳時,無法知道單個文件的上傳進(jìn)度,因為文件上傳消息中根本就沒有關(guān)于單個文件大小的信息
文件上傳消息
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 22 Apr 2014 07:45:45 GMT
POST /upload/uploadServlet HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:28.0) Gecko/20100101 Firefox/28.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://localhost:8080/upload/apacheUploadDemo.html
Cookie: JSESSIONID=33498CE814284D67F957CA53D45F0174
Connection: keep-alive
Content-Length 2363
Content-Type multipart/form-data; boundary=---------------------------189163093917262
-----------------------------189163093917262
Content-Disposition: form-data; name="name"?
-----------------------------189163093917262
Content-Disposition: form-data; name="file1"; filename="New Text Document.txt" Content-Type: text/plain
文件數(shù)據(jù)
-----------------------------189163093917262
Content-Disposition: form-data; name="file2"; filename="New Text Document (2).txt" Content-Type: text/plain
文件數(shù)據(jù)
-----------------------------189163093917262--
??
?2. 瀏覽器必須將所有文件讀取完畢才開始上傳,并且是一次性提交所有的數(shù)據(jù)文件,在互聯(lián)網(wǎng)環(huán)境下,會http連接超時,大文件無法上傳成功。
?
3. 服務(wù)端判斷是否超過大小限制,是通過計算接收數(shù)據(jù)的累積字節(jié)數(shù)和限制大小比較,這種情況下,如果限制大小是30M,那么在服務(wù)端已經(jīng)讀取了30M完成后才會拋出異常,多余的消耗的服務(wù)器的內(nèi)存和硬盤空間
?
所以基于這些原因,頁面表單 + RFC1897規(guī)范 + http協(xié)議上傳 + 后臺apache common upload組件接收的這種解決方案,不適合解決WEB頁面一次多文件上傳,大文件上傳情況,比較適合一次單個小文件附件的情況,如:博客附件,登記照片上傳,預(yù)覽等情況。
總結(jié)
以上是生活随笔為你收集整理的WEB文件上传之apache common upload使用(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。