java基于servlet实现文件上传功能解析
最近项目需要做一个文件上传功能,做完了分享下,顺带当做笔记。
上传功能用后台用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("×tamp=" + (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程序设计有所帮助。
上一篇: java 多态性详解及常见面试题