java基于servlet實現(xiàn)文件上傳功能解析
最近項目需要做一個文件上傳功能,做完了分享下,順帶當做筆記。
上傳功能用后臺用java實現(xiàn),前端主要是js的ajax實現(xiàn)。后臺還加入定時刪除臨時文件。
效果如圖
首先是上傳功能的主要類,下面是代碼
package util.upload; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.UUID; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; public class UploadServlet extends HttpServlet { private static final long serialVersionUID = -3100028422371321159L; private boolean isAllowed; private String upFileName; //定義合法后綴名的數(shù)組 private String[] allowedExtName=new String[] {"zip","rar",//壓縮文件 "txt","doc","wps","docx","java",//文本 "xls","xlsx",//表格 "ppt","pptx",//幻燈片 "pdf",//pdf "jpg","jpeg","bmp","gif","png"http://圖片 }; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //設(shè)置編碼格式為utf-8 request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8"); //獲取session,保存進度和上傳結(jié)果,上傳開始為nok,當為Ok表示上傳完成 HttpSession session=request.getSession(); session.setAttribute("result", "nok"); session.setAttribute("error", ""); String error=""; upFileName=""; isAllowed=false; //給上傳的文件設(shè)一個最大值,這里是不得超過100MB int maxSize=100*1024*1024; //創(chuàng)建工廠對象和文件上傳對象 DiskFileItemFactory factory=new DiskFileItemFactory(); ServletFileUpload upload=new ServletFileUpload(factory); //創(chuàng)建上傳監(jiān)聽器和設(shè)置監(jiān)聽器 UploadListener listener=new UploadListener(); session.setAttribute("LISTENER", listener); upload.setProgressListener(listener); //上傳路徑 String path = request.getSession().getServletContext().getRealPath("/upload"); String requestPath = request.getSession().getServletContext().getContextPath()+"/upload"; File dirFile =new File(path); //System.out.println(request.getSession().getServletContext().getContextPath()); //如果文件夾不存在則創(chuàng)建 if (!dirFile .exists() && !dirFile .isDirectory()) { dirFile .mkdir(); } //根據(jù)日期創(chuàng)建文件夾,保存到對應(yīng)日期的文件夾下 Date date=new Date(); SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMdd"); String subDirName=sdf.format(date); File subDirFile=new File(path+"/"+subDirName); if (!subDirFile .exists() && !subDirFile .isDirectory()) { subDirFile .mkdir(); } try { //解析上傳請求 List<FileItem> items=upload.parseRequest(request); Iterator<FileItem> itr=items.iterator(); while(itr.hasNext()){ FileItem item=(FileItem)itr.next(); //判斷是否為文件域 if(!item.isFormField()){ if(item.getName()!=null&&!item.getName().equals("")){ //獲取上傳文件大小和文件名稱 long upFileSize=item.getSize(); String fileName=item.getName(); //獲取文件后綴名 String[] splitName=fileName.split("\\."); String extName=splitName[splitName.length-1]; //檢查文件后綴名 for(String allowed:allowedExtName) { if(allowed.equalsIgnoreCase(extName)) { isAllowed=true; } } if(!isAllowed){ error="上傳文件格式不合法!"; break; } if(upFileSize>maxSize){ error="您上傳的文件太大了,請選擇不超過100MB的文件!"; break; } //此時文件暫存在服務(wù)器的內(nèi)存中,構(gòu)造臨時對象 File tempFile=new File(makeFileName(fileName)); //指定文件上傳服務(wù)器的目錄及文件名稱 File file=new File(path+"/"+subDirName+"/",tempFile.getName()); item.write(file);//第一種寫文件方法 upFileName=requestPath+"/"+subDirName+"/"+tempFile.getName(); if(upFileName.equals("")){ error="沒選擇上傳文件!"; } System.out.println(upFileName); /*//構(gòu)造輸入流讀文件 第二種寫文件方法 InputStream is=item.getInputStream(); int length=0; byte[] by=new byte[1024]; FileOutputStream fos=new FileOutputStream(file); while((length=is.read(by))!=-1){ fos.write(by, 0, length); //Thread.sleep(10); } fos.close(); //Thread.sleep(1000);*/ }else{ error="沒選擇上傳文件!"; } } } } catch (Exception e) { e.printStackTrace(); error="上傳文件出現(xiàn)錯誤:"+e.getMessage(); } if(!error.equals("")){ System.out.println(error); session.setAttribute("error", error); }else{ session.setAttribute("result", "OK"); session.setAttribute("filename",upFileName); } } /** * 為防止文件覆蓋的現(xiàn)象發(fā)生,要為上傳文件產(chǎn)生一個唯一的文件名 * @param filename 原文件名 * @return 生成的唯一文件名 */ private String makeFileName(String filename){ return UUID.randomUUID().toString() + "_" + filename; } }
其中需要引入commons-fileupload-1.3.1.jar,commons-io-2.4.jar
上傳過程中,我們需要實時獲取上傳進度等信息,引入的庫里為我們添加了一個ProgressListener接口,我們再寫一個類實現(xiàn)這個接口,上面類中添加該接口
//創(chuàng)建工廠對象和文件上傳對象 DiskFileItemFactory factory=new DiskFileItemFactory(); ServletFileUpload upload=new ServletFileUpload(factory); //創(chuàng)建上傳監(jiān)聽器和設(shè)置監(jiān)聽器 UploadListener listener=new UploadListener(); session.setAttribute("LISTENER", listener); upload.setProgressListener(listener);
下面是這個監(jiān)聽類的具體實現(xiàn)代碼
package util.upload; import org.apache.commons.fileupload.ProgressListener; public class UploadListener implements ProgressListener{ private volatile long bytesRead = 0L,//上傳的字節(jié)數(shù) contentLength = 0L,//總字節(jié)數(shù) item = 0L; public UploadListener() { super(); } @Override public void update(long aBytesRead, long aContentLength, int anItem) { bytesRead = aBytesRead; contentLength = aContentLength; item = anItem; } public long getBytesRead() { return bytesRead; } public long getContentLength() { return contentLength; } public long getItem() { return item; } }
現(xiàn)在能獲取上傳進度等信息了,但還需要一個servlet返回給前端,下面實現(xiàn)
package util.upload; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.fileupload.ProgressListener; import com.google.gson.Gson; /** 獲取上傳進度,上傳路徑,錯誤,上傳結(jié)果等信息 */ public class GetProgressServlet extends HttpServlet{ private static final long serialVersionUID = -3596466520775012991L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8"); UploadListener listener= null; HttpSession session = request.getSession(); String error=(String) session.getAttribute("error"); String result= (String) session.getAttribute("result"); String fileName=(String) session.getAttribute("filename"); PrintWriter out = response.getWriter(); long bytesRead = 0,contentLength = 0; if (session != null) { listener = (UploadListener)session.getAttribute("LISTENER"); if (listener == null) { return; } else { bytesRead = listener.getBytesRead();//上傳的字節(jié)數(shù) contentLength = listener.getContentLength();//總字節(jié)數(shù) } //自己定義的返回格式 String rp=bytesRead+"," +contentLength+"," +error+"," +result+"," +fileName; //System.out.println(rp); out.print(rp); /* //返回json格式數(shù)據(jù) Map<String,Object> map=new HashMap<String,Object>(); map.put("bytesRead", bytesRead); map.put("contentLength", contentLength); map.put("error", error); map.put("result", result); map.put("fileName", fileName); Gson gson=new Gson(); String json=gson.toJson(map); out.print(json);*/ out.flush(); out.close(); } } }
后臺上傳的功能代碼寫完了,下面實現(xiàn)上傳的前端,首先是html
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <script type="text/javascript" src="js/upfile.js" charset="utf-8"></script> <link rel="stylesheet" type="text/css" href="css/upfile.css"> </head> <body > <a href="javascript:addOne()">添加</a> <div id="target"> <input type="file" id="file" name="file" onchange="addfile(event)" multiple/> </div> <span id="test">0</span> </body> </html>
界面比較簡單,就一個添加的a標簽,負責上傳的input隱藏起來
css文件主要渲染的上傳進度的顯示
#file { display: none; } .pro{ width:500px; } .pborder { position: relative; width: 500px; /* 寬度 */ border: 1px solid #B1D632; padding: 1px; } .drawpro { width: 0px; display: block; position: relative; background: #B1D632; color: #333333; height: 20px; /* 高度 */ line-height: 20px; /* 必須和高度一致,文本才能垂直居中 */ } .pspan { position: absolute; width: 500px; text-align: center; font-weight: bold; }
接著是前端的重點,js文件
//顯示上傳信息的html var upfile_html = '<div class="pborder"><div class="drawpro">' + '<span class="pspan">0%</span></div></div><span name="path"></span><img src="common/upload/images/del.png" style="float:right" width="20" height="20" name="del" onclick=abortUpload(this)>'; var targetDIV_id = "target";//顯示上傳文件的目標div的ID var httpXML = null;//發(fā)送上傳請求的XMLHttpRequest對象 var httpProgress = null;//發(fā)送請求進度信息的XMLHttpRequest對象 var oldFileList = new Array();//修改時保存已有附件信息的列表 var uplist = new Array();//保存上傳文件的列表 var f_input;//上傳文件的input對象 var flag = true;//是否可以上傳下一個文件標志 var uurl = "Upload";//上傳文件的請求url var gurl = "getProgress";//獲取上傳進度信息的url var cancelFlag = 0;//取消標志 var timer, waittimer;//定時器 var nowID = 0;//正在上傳文件的id var ID = 0;//隊列中最后一個文件的id /** * 文件對象 */ function UploadFile(id, file) { this.id = id; this.file = file; this.state = 0; this.path = ""; } /** * 初始化的方法 */ window.onload = function init() { f_input = document.getElementById("file"); var tdiv = document.getElementById(targetDIV_id); var oldspan = tdiv.getElementsByTagName("SPAN"); for ( var i = 0; i < oldspan.length; i++) { oldFileList.push(oldspan[i].getAttribute("name")); } } /** * 選擇一個文件上傳 */ function addOne() { f_input.value = null; f_input.click(); } /** * 選中文件后將文件對象添加到隊列,開始上傳 * */ function addfile(evt) { var f = f_input.files[0]; if (f != undefined) { var uf = new UploadFile(ID, f); uplist.push(uf); var div = document.createElement("DIV"); div.setAttribute("id", "pro" + (ID)); div.setAttribute("class", "pro"); div.innerHTML = upfile_html; var targetDiv = document.getElementById(targetDIV_id); targetDiv.appendChild(div); div.getElementsByTagName("SPAN")[1].innerHTML = "文件名:" + uplist[ID].file.name; waittimer = setInterval("upload()", 1000); ID++; } } /** * 將隊列中的文件上傳 */ function upload() { if (flag == true) { if (uplist.length > 0) { var uf; for ( var i = 0; i < uplist.length; i++) { if (uplist[i].state == 0) { uf = uplist[i]; uplist[i].state = 1; break; } } if (uf != undefined & uf != null) { flag = false; if (window.XMLHttpRequest) { httpUP = new XMLHttpRequest(); } else if (window.ActiveXObject) { httpUP = new ActiveXObject("Microsoft.XMLHTTP"); } var formData = new FormData(); formData.append("file", uf.file); httpUP.open("POST", uurl, true); httpUP.upload.addEventListener('progress', uploadProgress, false); httpUP.send(formData); nowID = uf.id; timer = setInterval("getP()", 50); } } } } /** * 獲取上傳進度等信息 */ function getP() { if (window.XMLHttpRequest) { httpProgress = new XMLHttpRequest(); } else if (window.ActiveXObject) { httpProgress = new ActiveXObject("Microsoft.XMLHTTP"); } httpProgress.onreadystatechange = onProgress; httpProgress.open("post", gurl, true); httpProgress.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); httpProgress.send("&timeStamp=" + (new Date()).getTime()); } /** * 處理返回的上傳信息,顯示到界面 */ function onProgress() { if (httpProgress.readyState == 4 && httpProgress.status == 200) { result = httpProgress.responseText; var result = result.replace(/(^\s*)|(\s*$)/g, ""); var res = result.split(","); var now = parseInt(res[0]); var all = parseInt(res[1]); var err = res[2]; var state = res[3]; var path = res[4]; var per = (now / all * 100).toFixed(2); var prodiv = document.getElementById("pro" + nowID); if (prodiv != null & prodiv != undefined) { if (err != "" & err != null & err.length > 0) { window.clearInterval(timer); if (cancelFlag == 1) { err = "上傳終止"; cancelFlag = 0; } prodiv.getElementsByTagName("DIV")[0].style.display = "none"; prodiv.getElementsByTagName("SPAN")[1].innerHTML = err; httpUP.abort(); flag = true; uplist[nowID].state = 3; return; } if (state == "OK") { prodiv.getElementsByTagName("DIV")[0].style.display = "none"; var tmpf = uplist[nowID].file; prodiv.getElementsByTagName("SPAN")[1].innerHTML = "文件名:" + tmpf.name; window.clearInterval(timer); flag = true; uplist[nowID].state = 2; uplist[nowID].path = path; return; } prodiv.getElementsByTagName("DIV")[1].style.width = per * 5 + "px"; prodiv.getElementsByTagName("SPAN")[0].innerHTML = per + "%"; } } } /** * 取消上傳的方法 */ function abortUpload(obj) { var idStr = obj.parentNode.id; var id = idStr.slice(3); if (uplist[id].state == 1) { httpUP.abort(); flag = true; cancelFlag = 1; } else { uplist[id].state = 3; } document.getElementById(idStr).remove(); } /** * 獲取上傳文件的路徑 * @returns 格式化后字符串 */ function getFileListStr() { var str = ""; if (oldFileList.length > 0) { for ( var i = 0; i < oldFileList.length; i++) { if (oldFileList[i] != null & oldFileList[i] != "" & oldFileList[i] != undefined) { str = str + oldFileList[i] + ","; } } } for ( var i = 0; i < uplist.length; i++) { var f = uplist[i]; if (f.state == 2) { str = str + f.path + ","; } } return str; } /** * 移除修改時已有的舊附件 * */ function removeOld(btn) { var num = btn.getAttribute("name"); oldFileList[num - 1] = null; btn.parentNode.remove(); } function uploadProgress(e) { if (e.lengthComputable) { var iBytesUploaded = e.loaded; var iBytesTotal = e.total; document.getElementById("test").innerHTML=iBytesUploaded+"/"+iBytesTotal; } }
使用ajax發(fā)送上傳文件,獲取上傳進度,結(jié)果等信息。
使用的html5的file API,所以必須ie9以上的才可以兼容,火狐還有個問題,ajax請求不立即返回,直到所有ajax請求都發(fā)送完了,才都返回同一個結(jié)果,這就導致上傳進度不顯示。不過上傳進度信息也可以使用html5的file api獲取,其中加了一點代碼,頁面下面test的div里的數(shù)值就是在前端獲取到的進度。
上傳的都實現(xiàn)完了,接著是處理上傳后的臨時文件,因為使用的uuid命名文件,所以文件會生成很多,沒用的需要定時處理。使用ServletContextListener:
在 Servlet API 中有一個 ServletContextListener 接口,它能夠監(jiān)聽 ServletContext 對象的生命周期,實際上就是監(jiān)聽 Web 應(yīng)用的生命周期。
當Servlet 容器啟動或終止Web 應(yīng)用時,會觸發(fā)ServletContextEvent 事件,該事件由ServletContextListener 來處理。在 ServletContextListener 接口中定義了處理ServletContextEvent 事件的兩個方法。
利用其特性,實現(xiàn)定時刪除臨時文件的功能,代碼如下:
package util.upload; import java.io.IOException; import java.io.InputStream; import java.util.Date; import java.util.Properties; import java.util.Timer; import java.util.TimerTask; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; /** * 時間監(jiān)聽器 * * */ public class TempFileListener implements ServletContextListener { private Timer timer; private SystemTaskTest systemTask; private static String every_time_run; static { Properties prop = new Properties(); InputStream inStrem = TempFileManager.class.getClassLoader() .getResourceAsStream("tempfile.properties"); try { prop.load(inStrem); System.out.println(inStrem); every_time_run = prop.getProperty("every_time_run"); } catch (IOException e) { e.printStackTrace(); } finally { try { inStrem.close(); } catch (IOException e) { e.printStackTrace(); } } } // 監(jiān)聽器初始方法 public void contextInitialized(ServletContextEvent sce) { timer = new Timer(); systemTask = new SystemTaskTest(sce.getServletContext() .getRealPath("/"), sce.getServletContext()); try { System.out.println("定時器已啟動"); // 監(jiān)聽器獲取網(wǎng)站的根目錄 String path = sce.getServletContext().getRealPath("/"); Long time = Long.parseLong(every_time_run) * 1000;// 循環(huán)執(zhí)行的時間 System.out.println("time" + time); // 第一個參數(shù)是要運行的代碼,第二個參數(shù)是從什么時候開始運行,第三個參數(shù)是每隔多久在運行一次。重復(fù)執(zhí)行 timer.schedule(systemTask, 10000, time); System.out.println("已經(jīng)添加任務(wù)調(diào)度表"); } catch (Exception e) { e.printStackTrace(); } } public void contextDestroyed(ServletContextEvent sce) { try { timer.cancel(); } catch (Exception e) { } } } /** * 時間任務(wù)器 * */ class SystemTaskTest extends TimerTask { private ServletContext context; private String path; public SystemTaskTest(String path, ServletContext context) { this.path = path; this.context = context; } /** * 把要定時執(zhí)行的任務(wù)就在run中 */ public void run() { TempFileManager etf; try { System.out.println("開始執(zhí)行任務(wù)!"); // 需要執(zhí)行的代碼 System.out.println(new Date().toLocaleString()); etf = new TempFileManager(path); etf.run(); System.out.println("指定任務(wù)執(zhí)行完成!"); } catch (Exception e) { e.printStackTrace(); } } }
上面只是監(jiān)聽器,負責定時調(diào)用刪除臨時文件的方法,具體實現(xiàn)是下面的類
package util.upload; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Date; import java.util.Properties; /** * 刪除服務(wù)器上的文件 * */ public class TempFileManager implements Runnable { private String path;//路徑 private static String RETENTION_TIME = "1440";// 文件保存的時間 一天單位分 static { Properties prop = new Properties(); InputStream inStrem = TempFileManager.class.getClassLoader() .getResourceAsStream("execl.properties"); try { prop.load(inStrem); RETENTION_TIME = prop.getProperty("file_retention_time"); } catch (IOException e) { e.printStackTrace(); } finally { try { inStrem.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 構(gòu)造函數(shù)。初始化參數(shù) * @param path */ public TempFileManager(String path) { this.path = path; } /** * 把線程要執(zhí)行的代碼放在run()中 */ public void run() { System.out.println("文件管理開始========="); path = path + "upload"; System.out.println("文件管理路徑===" + path); File file = new File(path); deletefiles(file); } /** * 批量刪除文件 * * @param folder */ public void deletefiles(File folder) { if(folder.isDirectory()){ File[] files = folder.listFiles(); if(files.length<=0){ if(!folder.getAbsolutePath().equalsIgnoreCase(path)){ if(canDeleteFile(folder)){ if (folder.delete()) { System.out.println("文件夾" + folder.getName() + "刪除成功!"); } else { System.out.println("文件夾" + folder.getName() + "刪除失敗!此文件夾內(nèi)的文件可能正在被使用"); } } } } for (int i = 0; i < files.length; i++) { if(files[i].isDirectory()) { deletefiles(files[i]); }else{ deleteFile(files[i]); } } } } /** * 刪除文件 * * @param file */ private void deleteFile(File file) { try { if (file.isFile()) { // 刪除符合條件的文件 if (canDeleteFile(file)) { if (file.delete()) { System.out.println("文件" + file.getName() + "刪除成功!"); } else { System.out.println("文件" + file.getName() + "刪除失敗!此文件可能正在被使用"); } } else { } } else { System.out.println("沒有可以刪除的文件了"); } } catch (Exception e) { System.out.println("刪除文件失敗========"); e.printStackTrace(); } } /** * 判斷文件是否能夠被刪除 */ private boolean canDeleteFile(File file) { Date fileDate = getfileDate(file); Date date = new Date(); long time = (date.getTime() - fileDate.getTime()) / 1000 / 60 - Integer.parseInt(RETENTION_TIME);// 當前時間與文件間隔的分鐘 // System.out.println("time=="+time); if (time > 0) { return true; } else { return false; } } /** * 獲取文件最后的修改時間 * * @param file * @return */ private Date getfileDate(File file) { long modifiedTime = file.lastModified(); Date d = new Date(modifiedTime); return d; } }
判斷文件是否超時,超時就自動刪除,并且能自動刪除文件夾。
以上就是本文的全部內(nèi)容,希望對大家學習java程序設(shè)計有所幫助。
- JavaServlet的文件上傳和下載實現(xiàn)方法
- Java Servlet簡單實例分享(文件上傳下載demo)
- java基于servlet編寫上傳下載功能 類似文件服務(wù)器
- Java Servlet上傳圖片到指定文件夾并顯示圖片
- java基于servlet的文件異步上傳
- java基于servlet實現(xiàn)文件上傳功能
- 基于java文件上傳-原始的Servlet方式
- java基于servlet使用組件smartUpload實現(xiàn)文件上傳
- java web中的servlet3 upload上傳文件實踐
- 簡單操作實現(xiàn)Java jsp servlet文件上傳過程解析
- JavaWeb servlet實現(xiàn)下載與上傳功能的方法詳解
相關(guān)文章
java動態(tài)添加外部jar包到classpath的實例詳解
這篇文章主要介紹了java動態(tài)添加外部jar包到classpath的實例詳解的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下2017-09-09基于Java網(wǎng)絡(luò)編程和多線程的多對多聊天系統(tǒng)
這篇文章主要介紹了基于Java網(wǎng)絡(luò)編程和多線程的多對多聊天系統(tǒng),文中有非常詳細的代碼示例,對正在學習java網(wǎng)絡(luò)編程的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-04-04詳解SSM框架下結(jié)合log4j、slf4j打印日志
本篇文章主要介紹了詳解SSM框架下結(jié)合log4j、slf4j打印日志,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-11-11Java數(shù)據(jù)結(jié)構(gòu)之對象的比較
比較對象是面向?qū)ο缶幊陶Z言的一個基本特征,下面這篇文章主要給大家介紹了關(guān)于Java數(shù)據(jù)結(jié)構(gòu)之對象的比較,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-02-02