最近项目需要做一个文件上传功能,做完了分享下,顺带当做笔记。
上传功能用后台用java实现,前端主要是js的ajax实现。后台还加入定时删除临时文件。
效果如图
首先是上传功能的主要类,下面是代码
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; //定义合法后缀名的数组 private String[] allowedExtName=new String[] {\"zip\",\"rar\",//压缩文件 \"txt\",\"doc\",\"wps\",\"docx\",\"java\",//文本 \"xls\",\"xlsx\",//表格 \"ppt\",\"pptx\",//幻灯片 \"pdf\",//pdf \"jpg\",\"jpeg\",\"bmp\",\"gif\",\"png\"//图片 }; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //设置编码格式为utf-8 request.setCharacterEncoding(\"utf-8\"); response.setCharacterEncoding(\"utf-8\"); //获取session,保存进度和上传结果,上传开始为nok,当为Ok表示上传完成 HttpSession session=request.getSession(); session.setAttribute(\"result\", \"nok\"); session.setAttribute(\"error\", \"\"); String error=\"\"; upFileName=\"\"; isAllowed=false; //给上传的文件设一个最大值,这里是不得超过100MB int maxSize=100*1024*1024; //创建工厂对象和文件上传对象 DiskFileItemFactory factory=new DiskFileItemFactory(); ServletFileUpload upload=new ServletFileUpload(factory); //创建上传监听器和设置监听器 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()); //如果文件夹不存在则创建 if (!dirFile .exists() && !dirFile .isDirectory()) { dirFile .mkdir(); } //根据日期创建文件夹,保存到对应日期的文件夹下 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; } //此时文件暂存在服务器的内存中,构造临时对象 File tempFile=new File(makeFileName(fileName)); //指定文件上传服务器的目录及文件名称 File file=new File(path+\"/\"+subDirName+\"/\",tempFile.getName()); item.write(file);//第一种写文件方法 upFileName=requestPath+\"/\"+subDirName+\"/\"+tempFile.getName(); if(upFileName.equals(\"\")){ error=\"没选择上传文件!\"; } System.out.println(upFileName); /*//构造输入流读文件 第二种写文件方法 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=\"上传文件出现错误:\"+e.getMessage(); } if(!error.equals(\"\")){ System.out.println(error); session.setAttribute(\"error\", error); }else{ session.setAttribute(\"result\", \"OK\"); session.setAttribute(\"filename\",upFileName); } } /** * 为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名 * @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接口,我们再写一个类实现这个接口,上面类中添加该接口
//创建工厂对象和文件上传对象 DiskFileItemFactory factory=new DiskFileItemFactory(); ServletFileUpload upload=new ServletFileUpload(factory); //创建上传监听器和设置监听器 UploadListener listener=new UploadListener(); session.setAttribute(\"LISTENER\", listener); upload.setProgressListener(listener);
下面是这个监听类的具体实现代码
package util.upload; import org.apache.commons.fileupload.ProgressListener; public class UploadListener implements ProgressListener{ private volatile long bytesRead = 0L,//上传的字节数 contentLength = 0L,//总字节数 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; } }
现在能获取上传进度等信息了,但还需要一个servlet返回给前端,下面实现
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; /** 获取上传进度,上传路径,错误,上传结果等信息 */ 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();//上传的字节数 contentLength = listener.getContentLength();//总字节数 } //自己定义的返回格式 String rp=bytesRead+\",\" +contentLength+\",\" +error+\",\" +result+\",\" +fileName; //System.out.println(rp); out.print(rp); /* //返回json格式数据 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(); } } }
后台上传的功能代码写完了,下面实现上传的前端,首先是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;//发送上传请求的XMLHttpRequest对象 var httpProgress = null;//发送请求进度信息的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发送上传文件,获取上传进度,结果等信息。
使用的html5的file API,所以必须ie9以上的才可以兼容,火狐还有个问题,ajax请求不立即返回,直到所有ajax请求都发送完了,才都返回同一个结果,这就导致上传进度不显示。不过上传进度信息也可以使用html5的file api获取,其中加了一点代码,页面下面test的div里的数值就是在前端获取到的进度。
上传的都实现完了,接着是处理上传后的临时文件,因为使用的uuid命名文件,所以文件会生成很多,没用的需要定时处理。使用ServletContextListener:
在 Servlet API 中有一个 ServletContextListener 接口,它能够监听 ServletContext 对象的生命周期,实际上就是监听 Web 应用的生命周期。
当Servlet 容器启动或终止Web 应用时,会触发ServletContextEvent 事件,该事件由ServletContextListener 来处理。在 ServletContextListener 接口中定义了处理ServletContextEvent 事件的两个方法。
利用其特性,实现定时删除临时文件的功能,代码如下:
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; /** * 时间监听器 * * */ 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(); } } } // 监听器初始方法 public void contextInitialized(ServletContextEvent sce) { timer = new Timer(); systemTask = new SystemTaskTest(sce.getServletContext() .getRealPath(\"/\"), sce.getServletContext()); try { System.out.println(\"定时器已启动\"); // 监听器获取网站的根目录 String path = sce.getServletContext().getRealPath(\"/\"); Long time = Long.parseLong(every_time_run) * 1000;// 循环执行的时间 System.out.println(\"time\" + time); // 第一个参数是要运行的代码,第二个参数是从什么时候开始运行,第三个参数是每隔多久在运行一次。重复执行 timer.schedule(systemTask, 10000, time); System.out.println(\"已经添加任务调度表\"); } catch (Exception e) { e.printStackTrace(); } } public void contextDestroyed(ServletContextEvent sce) { try { timer.cancel(); } catch (Exception e) { } } } /** * 时间任务器 * */ class SystemTaskTest extends TimerTask { private ServletContext context; private String path; public SystemTaskTest(String path, ServletContext context) { this.path = path; this.context = context; } /** * 把要定时执行的任务就在run中 */ public void run() { TempFileManager etf; try { System.out.println(\"开始执行任务!\"); // 需要执行的代码 System.out.println(new Date().toLocaleString()); etf = new TempFileManager(path); etf.run(); System.out.println(\"指定任务执行完成!\"); } catch (Exception e) { e.printStackTrace(); } } }
上面只是监听器,负责定时调用删除临时文件的方法,具体实现是下面的类
package util.upload; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Date; import java.util.Properties; /** * 删除服务器上的文件 * */ 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(); } } } /** * 构造函数。初始化参数 * @param path */ public TempFileManager(String path) { this.path = path; } /** * 把线程要执行的代码放在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() + \"删除失败!此文件夹内的文件可能正在被使用\"); } } } } 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; } }
判断文件是否超时,超时就自动删除,并且能自动删除文件夹。
以上就是本文的全部内容,希望对大家学习java程序设计有所帮助。
本文地址:https://www.stayed.cn/item/14131
转载请注明出处。
本站部分内容来源于网络,如侵犯到您的权益,请 联系我