说明位于src类下面
Apache开源组织提供的处理文件上传的组件: commons-fileupload-1.2.1.jar commons-io-1.4.jar 简单步骤,处理上传文件数据 第1步,首先new 一个未经过配置的工厂实例DiskFileItemFactory 第2步,用提供的工厂创建一个解析上传文件的解析器实例ServletFileUpload!! 第3步,用解析器的parseRequest方法,解析request中提交的数据 每个表单项都封装成一个FileItem对象,加入list集合! 第4步,迭代list集合 第5步,isFormField判断普通字段还是上传字段 如果是上传字段getInputStream,一顿狂写 下面是十大上传文件的细节 1.上传文件的中文乱码 1.1 解决上传文件的文件名的中文乱码 ServletFileUpload.setHeaderEncoding("UTF-8") 1.2 解决普通输入项的值的中文乱码 (注意,表单类型为multipart/form-data的时候,设置request的编码是无效的) FileItem.setString("UTF-8"); //解决乱码 2.在处理表单之前,要记得调用: ServletFileUpload.isMultipartContent 静态方法判断提交表单的类型, 如果该方法返回true,则按上传方式处理,否 则按照传统方式处理表单即可。request.getParameter("username")... 3.设置解析器缓冲区的大小,以及临时文件的删除 设置解析器缓冲区的大小 DiskFileItemFactory.setSizeThreshold(1024*1024); 超过临界大小,需设置临时目录:DiskFileItemFactory.setRepository(new File(this.getServletContext().getRealPath("/temp"))); 临时文件的删除:在程序中处理完上传文件后,一定要记得调用item.delete()方法,以删除临时文件,必须是在关闭流之后,finally代码块中,确保删除成功! 4.在做上传系统时,千万要注意上传文件的保存目录,这个上传文件的保存目录绝对不能让外界直接访问到。放到WEB-INF中保护起来!避免别人上传jsp脚本直接运行 5.限制上传文件的类型 在处理上传文件时,判断上传文件的后缀名是不是允许的 6.限制上传文件的大小 调用解析器的ServletFileUpload.setFileSizeMax(1024*1024*5);就可以限制上传文件的大小,如果上传文件超出限制,则解析器会抛FileUploadBase.FileSizeLimitExceededException异常,程序员通过是否抓到这个异常,进而就可以给用户友好提示。 7.如何判断空的上传输入项 String filename = item.getName().substring(item.getName().lastIndexOf("\\")+1); //"" if(filename==null || filename.trim().equals("")){ continue; } 8、为避免上传文件的覆盖,程序在保存上传文件时,要为每一个文件生成一个唯一的文件名 public String generateFileName(String filename){ //83434-83u483-934934 return UUID.randomUUID().toString() + "_" + filename; } 9、为避免在一个文件夹下面保存超过1000个文件,影响文件访问性能,程序应该把上传文件打散后存储。 public String generateSavePath(String path,String filename){ int hashcode = filename.hashCode(); //121221 int dir1 = hashcode&15; int dir2 = (hashcode>>4)&0xf; String savepath = path + File.separator + dir1 + File.separator + dir2; File file = new File(savepath); if(!file.exists()){ file.mkdirs(); } return savepath; } 10、监听上传进度 ServletFileUpload upload = new ServletFileUpload(factory); upload.setProgressListener(new ProgressListener(){ public void update(long pBytesRead, long pContentLength, int pItems) { System.out.println("当前已解析:" + pBytesRead); } }); 高级版本 //细节:上传进度,前台用AJAX,后台注册一个监听器 ProgressListener lisen=new ProgressListener(){ //实现每1M更新一次,算法经典~ private long flag=-1; public void update(long bytesRead, long contentLength, int items) { //contentLength表示所有文件总大小 long full=bytesRead/(1000*1000); if (flag==full) { //开始时尚不足1M时,full=0,flag=-1,不作任何处理 return; } //当full每满1M时,将flag=full,便于下1M的时候判断 flag=full; System.out.println("正在处理第"+items+"个文件"); //items从1开始算起~ if (contentLength==-1) { System.out.println("总共上传了:"+bytesRead+"字节"); } else { System.out.println("已上传了:"+bytesRead+"of"+contentLength+"字节"); } } }; //别忘记给upload设置一个监听器 upload.setProgressListener(lisen); 11、在web页面中添加动态上传输入项 12、下载时解决中文乱码 //下载之前,要先设置response的头字段, //通知浏览器,以下载文件的方式打开, //注意,解决文件名的中文乱码(用URLEncoder指定utf-8编码) //注意,不能倒着取,因为有可能文件名中也有下划线XX-XX-XX_a_1.txt //得到文件的原始文件名simpleName如 a_1.txt String simpleName=full_name.substring(full_name.indexOf("_")+1); //重点!通知浏览器以下载方式打开下面发送的数据 response.setHeader("content-disposition","attachment;filename="+URLEncoder.encode(simpleName, "utf-8"));
package cn.itcast.web.controller; import java.io.IOException; import java.io.InputStream; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class UploadServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /*如果表单的enctype属性值为multipart/form-data的话, 在servlet中注意就不能采用getParameter传统方式获取数据 那样,只会返回null String username=request.getParameter("username"); System.out.println(username);null*/ InputStream in=request.getInputStream(); byte[] buf=new byte[1024]; int len=0; while((len=in.read(buf))!=-1){ System.out.println(new String(buf,0,len)); } in.close(); /*下面是表单的enctype属性值为multipart/form-data的时候Post体的值 -----------------------------7de1f327061c Content-Disposition: form-data; name="username" sg -----------------------------7de1f327061c Content-Disposition: form-data; name="file_1"; filename="a.txt" Content-Type: text/plain aaaaa -----------------------------7de1f327061c Content-Disposition: form-data; name="file_2"; filename="b.txt" Content-Type: text/plain bbbbb -----------------------------7de1f327061c--*/ } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } /*MIME协议: POST /day18/servlet/UploadServlet HTTP/1.1 Accept: Referer: http://localhost:8080/day18/upload.jsp Accept-Language: zh-cn User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727) Content-Type: multipart/form-data; boundary=---------------------------7de1282d74043a Accept-Encoding: gzip, deflate Host: localhost:8080 Content-Length: 440 Connection: Keep-Alive Cache-Control: no-cache Cookie: JSESSIONID=F05C1BDA9A447043CFB175DD4A5E8916 -----------------------------7de1282d74043a Content-Disposition: form-data; name="username" sg -----------------------------7de1282d74043a Content-Disposition: form-data; name="file_1"; filename="a.txt" Content-Type: text/plain aaaa -----------------------------7de1282d74043a Content-Disposition: form-data; name="file_2"; filename="b.txt" Content-Type: text/plain bbbbb -----------------------------7de1282d74043a-- */
UploadServlet2位于web.controller包
package cn.itcast.web.controller; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; 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.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; public class UploadServlet2 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //下面是简单地处理上传文件过程~ /*简单步骤,处理上传文件数据 第1步,首先new 一个未经过配置的工厂实例DiskFileItemFactory 第2步,用提供的工厂创建一个解析上传文件的解析器实例ServletFileUpload!! 第3步,用解析器的parseRequest方法,解析request中提交的数据 每个表单项都封装成一个FileItem对象,加入list集合! 第4步,迭代list集合 第5步,isFormField判断普通字段还是上传字段 如果是上传字段getInputStream,一顿狂写*/ //servlet的doPost方法try异常模板代码[try_post] try { //第1步,首先new 一个未经配置的工厂实例! DiskFileItemFactory factory =new DiskFileItemFactory(); //第2步,用提供的工厂作参数,创建一个解析上传文件的解析器实例! ServletFileUpload upload=new ServletFileUpload(factory); //第3步,用解析器的parseRequest方法,解析request里的数据 //内部会将每个表单项都封装成一个FileItem对象,加入list集合! List<FileItem> list=upload.parseRequest(request); int i=0; for (FileItem item : list) { //先判断是否为简单的表单字段 if (item.isFormField()) { //true表是 a simple form field String fieldName=item.getFieldName(); //注意getString是针对普通字段,取其值的 String fieldValue=item.getString(); System.out.println("字段名:"+fieldName); System.out.println("username:"+fieldValue); } else { //false代表是 an uploaded file //代表当前处理的item里面封装的是上传文件 File dest=new File("C:\\upload\\"); //健壮性判断 if (!dest.exists()) { dest.mkdirs(); } //注意getName是针对上传文件,取其文件名的 // item.getName() 如果是IE6 C:\Documents and Settings\Admin\桌面\a.txt // item.getName() 如果是IE7 a.txt //所以要截取出来真正的上传文件名 String fileName=item.getName(); fileName=fileName.substring(fileName.lastIndexOf("\\")+1); //字节流必须用字节数组 byte[] buf = new byte[1024]; int len = 0; //getInputStream获取关联了上传文件内容的流 InputStream in=item.getInputStream(); //关联目的 OutputStream out = new FileOutputStream(new File("C:\\upload\\"+fileName)); //一顿狂写 while ((len = in.read(buf)) != -1) { out.write(buf, 0, len); } //最后记得关流,实际开发时,放finally 还要try起来 in.close(); out.close(); } } } catch (Exception e) { //出现异常不要紧,记录下来,并给友好提示! e.printStackTrace(); request.setAttribute("message", "XX失败"); request.getRequestDispatcher("/message.jsp").forward(request,response); } //一路无错,就是上传成功 request.setAttribute("message", "上传成功"); request.getRequestDispatcher("/message.jsp").forward(request,response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } /*String sql="C:\\Documents and Settings\\Administrator\\桌面\\a.txt"; System.out.println(sql.substring(sql.lastIndexOf("\\")+1)); 结果是:a.txt sql="b.txt"; System.out.println(sql.substring(sql.lastIndexOf("\\")+1)); 结果是:b.txt*/
package cn.itcast.web.controller; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; 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 org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.ProgressListener; import org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; /*简单步骤,处理上传文件数据 第1步,首先new 一个未经过配置的工厂实例DiskFileItemFactory 第2步,用提供的工厂创建一个解析上传文件的解析器实例ServletFileUpload!! 第3步,用解析器的parseRequest方法,解析request中提交的数据 每个表单项都封装成一个FileItem对象,加入list集合! 第4步,迭代list集合 第5步,isFormField判断普通字段还是上传字段 如果是上传字段getInputStream,一顿狂写*/ public class UploadServlet3 extends HttpServlet { //成员标记仅用于判断上传的文件是不是全为空~ private boolean flag=false; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //下面是详细地处理上传文件过程~注意各类细节问题~ //定义一个List集合记住允许上传的文件后缀 //允许上传的文件后缀名如下: List<String> types=Arrays.asList("jpg","bmp","gif","png","txt","avi","pdf","mkv","mp3"); try { //第1步,首先new 一个未经配置的工厂实例! DiskFileItemFactory factory =new DiskFileItemFactory(); //细节:解析器缓冲区的大小,如小于10K,直接写硬盘 //工厂设置上传文件缓冲区大小的临界值:单位是K,默认是10K factory.setSizeThreshold(1024*1024); //单位是字节,现在是1M //上面设置了上传文件大小 的临界值是1兆, //当文件大小超过1兆的时候,会先写到临时目录下 //当上传文件过大时,便会用到临时文件夹(应自己确保临时文件的删除) factory.setRepository(new File(this.getServletContext().getRealPath("/temp"))); //代表当前web应用,给服务器看 //上传完后记得调用FileItem.delete()删除,必须是在关闭流之后,finally代码块中,确保删除成功! //第2步,用提供的工厂作参数,创建一个解析上传文件的解析器实例! ServletFileUpload upload=new ServletFileUpload(factory); //正式解析之前,必须先设置单个文件的大小限制!5M upload.setFileSizeMax(1024*1024*50); //ServletFileUpload的静态方法isMultipartContent(request) //判断request,是否为上传文件的表单(enctype="mutipart/form-data") if(!ServletFileUpload.isMultipartContent(request)){ //细节:如果表单不是upload file,那么就可以按传统方式处理 //只有普通表单,request.setCharacterEncoding("utf-8")才有效果 request.setCharacterEncoding("utf-8"); String value=request.getParameter("username"); System.out.println(value); return; } //细节:在解析request之前,必须先解决上传文件名的中文乱码问题 upload.setHeaderEncoding("UTF-8"); //细节:上传进度,前台用AJAX,后台注册一个监听器 ProgressListener lisen=new ProgressListener(){ //实现每1M更新一次,算法经典~ private long flag=-1; public void update(long bytesRead, long contentLength, int items) { //contentLength表示所有文件总大小 long full=bytesRead/(1000*1000); if (flag==full) { //开始时尚不足1M时,full=0,flag=-1,不作任何处理 return; } //当full每满1M时,将flag=full,便于下1M的时候判断 flag=full; System.out.println("正在处理第"+items+"个文件"); //items从1开始算起~ if (contentLength==-1) { System.out.println("总共上传了:"+bytesRead+"字节"); } else { System.out.println("已上传了:"+bytesRead+"of"+contentLength+"字节"); } } }; //别忘记给upload设置一个监听器 upload.setProgressListener(lisen); //第3步,用解析器的parseRequest方法,解析request里的数据 //内部会将每个表单项都封装成一个FileItem对象,加入list集合! List<FileItem> list=upload.parseRequest(request); //System.out.println(list.size()); 什么都不填写时 是1 int i=0; for (FileItem item : list) { //先判断是否为简单的表单字段 if (item.isFormField()) { //true表是 a simple form field String fieldName=item.getFieldName(); //注意getString是针对普通字段,取其值的 //getString指定码表,可解决普通表单字段的值的中文乱码问题 //解决普通表单字段的值的中文乱码问题(如input name="username",value="中国") //String fieldValue=item.getString("UTF-8"); //上面指定码表的内部实现如下: //inputValue = new String(inputValue.getBytes("iso8859-1"),"UTF-8"); //或者自己手动解决普通表单字段的值的中文乱码问题 String fieldValue=item.getString(); fieldValue=new String(fieldValue.getBytes("iso8859-1"),"utf-8"); System.out.println("字段名:"+fieldName); System.out.println("username:"+fieldValue); } else { //注意getName是针对上传文件,取其文件名的 // item.getName() 如果是IE6 C:\Documents and Settings\Admin\桌面\a.txt // item.getName() 如果是IE7 a.txt //所以要截取出来真正的上传文件名 String fileName=item.getName(); //如果类型为file的input 没有内容,文件名就为空字符串! //服务器处理的时候需要先判断文件名是否为空字符串! //否则,流写数据的时候会抛FileNotFoundExcetpion,因为不能向目录写内容 fileName=fileName.substring(fileName.lastIndexOf("\\")+1); if (fileName==null || "".equals(fileName.trim())) { //继续处理下一个上传文件 continue; } //运行到此标志,证明有文件上传了~ flag=true; //正式处理上传文件之前,先判断文件名和文件大小 //在这儿点号不用转义 //先获取上传的文件的后缀如txt String ext=fileName.substring(fileName.lastIndexOf(".")+1); //再判断允许的后缀集合是否包含,如不包含,则跳转到全局消息 if(!types.contains(ext)){ //如果上传文件的类型不对,存消息,转发,return; request.setAttribute("message", "不支持后缀为."+ext+"的文件上传"); request.getRequestDispatcher("/message.jsp").forward(request,response); return; } //false代表是 an uploaded file //代表当前处理的item里面封装的是上传文件 //细节:处理重复文件问题,生成唯一的文件名,并且打散存储 //调用自定义方法:参数是a.txt,返回的全文件名是:X-X-X_a.txt String full_name=generateFullName(fileName); //下面是打散文件,分别存放到指定目录下(如/WEB-INF/upload目录) //参数是全球唯一的full_name,形如X-X-X_a.txt //在指定目录下,根据完整文件名的低四次,和次低四位生成文件夹 //返回绝对路径(即完整路径,含全文件名) String parentPath=this.getServletContext().getRealPath("/WEB-INF/upload"); String fullPath=generateChildPath(parentPath,full_name); InputStream in=null; OutputStream out=null; try { //字节流必须用字节数组 byte[] buf = new byte[1024]; int len = 0; //getInputStream获取关联了上传文件内容的流 in=item.getInputStream(); //关联目的 //out = new FileOutputStream(new File("C:\\upload\\"+fileName)); //文件所在目录,必须保护起来,坚决不能让外界访问 out = new FileOutputStream(new File(fullPath)); //一顿狂写 while ((len = in.read(buf)) != -1) { out.write(buf, 0, len); } }finally { if (in != null) { try { in.close(); } catch (IOException e) { throw new RuntimeException("in关闭失败!"); } in = null; } if (out != null) { try { out.close(); } catch (IOException e) { throw new RuntimeException("out关闭失败!"); } out = null; } //删除临时文件:在程序中处理完上传文件后, //一定要记得调用item.delete()方法,以删除临时文件 //必须是在关闭流之后,finally代码块中,确保删除成功! item.delete(); //虽然有时会自动删除临时文件 } } } }catch( FileSizeLimitExceededException e){ //如果上传文件的大小超过限制,存消息,转发,return; request.setAttribute("message", "不支持5M以上大小的文件上传"); request.getRequestDispatcher("/message.jsp").forward(request,response); return; }catch (Exception e) { //出现异常不要紧,记录下来,并给友好提示! e.printStackTrace(); request.setAttribute("message", "XX失败"); request.getRequestDispatcher("/message.jsp").forward(request,response); } if( !flag){ request.setAttribute("message", "您没有上传任何文件~"); request.getRequestDispatcher("/message.jsp").forward(request,response); return; } //一路无错,就是上传成功 request.setAttribute("message", "上传成功"); request.getRequestDispatcher("/message.jsp").forward(request,response); } //下面是打散文件,分别存放到指定目录下(如/WEB-INF/upload目录) //参数是全球唯一的full_name,形如X-X-X_a.txt //在指定目录下,根据完整文件名的低四次,和次低四位生成文件夹 //根据父目录和完整文件名称,返回绝对路径(即完整路径,含全文件名) private String generateChildPath(String parentPath, String full_name) { int hashCode=full_name.hashCode();//相同字符,哈希值一样,共4*8=32位 int dir1=hashCode&15; int dir2=(hashCode>>4)&0xf; //这样就可以保存16*1000*16个文件,如果不够用,还可以dir3 //因为部署到Linux系统,所以要做到与平台无关File.separator\ File file=new File(parentPath+File.separator+dir1+File.separator+dir2); //健壮性判断 if (!file.exists()) { //注意多层目录必须加S file.mkdirs(); } return file+File.separator+full_name; } //细节:处理重复文件问题,生成唯一的文件名, //自定义方法1:参数是a.txt,返回的全文件名是:X-X-X_a.txt //d92569e5-cb97-4305-8cc1-b72a5d91551b_a.txt private String generateFullName(String fileName) { return UUID.randomUUID().toString()+"_"+fileName; } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } /*String sql="C:\\Documents and Settings\\Administrator\\桌面\\a.txt"; System.out.println(sql.substring(sql.lastIndexOf("\\")+1)); 结果是:a.txt sql="b.txt"; System.out.println(sql.substring(sql.lastIndexOf("\\")+1)); 结果是:b.txt*/
package cn.itcast.web.controller; import java.io.File; import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; //列出网站所有可供下载的文件 //关系是如何保存迭代出来的文件名 public class ListFilesServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String parentPath=this.getServletContext().getRealPath("/WEB-INF/upload"); //迭代出upload目录下的所有文件, //保存到map集合,key是全球唯一的full_name,形如X-X-X_a.txt //value是singleName,形如a.txt Map<String, String> map=new LinkedHashMap<String, String>(); //调用递归方法,迭代出目录下的所有文件 File dir=new File(parentPath); listAllFiles_1(dir,map); System.out.println(map);//{} //健壮性判断 if (map.size()==0) { request.setAttribute("message", "暂无文件可供下载"); request.getRequestDispatcher("/message.jsp").forward(request, response); return; } //全部存到map之后,存到request带给jsp显示 request.setAttribute("map", map); request.getRequestDispatcher("/WEB-INF/jsp/listfiles.jsp").forward(request, response); } private void listAllFiles_2(File file, Map<String, String> map) { //老方的递归,先判断file是文件还是文件夹 //如果是文件夹,取出所有文件,继续递归~ //如果是文件,添加到map if (!file.isFile()) { File[] files=file.listFiles(); for (File f : files) { listAllFiles_2(f,map); } } else { //file.getName() 就是XX-XX-XX_a.txt //value就是a.txt //8347824284-343-343_a_b.txt map.put(file.getName(), file.getName().substring(file.getName().indexOf("_")+1)); //<a href="/servlet?filename=文件在服务器的UUID名称">文件的原始文件名(A.TXT)</a> } } private void listAllFiles_1(File dir, Map<String, String> map) { //老毕的递归,直接列出dir下所有文件迭代, //再判断是文件还是文件夹 //如果是文件夹,继续递归~ //如果是文件,添加到map File[] files=dir.listFiles(); for (File f : files) { if (!f.isFile()) { listAllFiles_1(f,map); } else { map.put(f.getName(), f.getName().substring(f.getName().indexOf("_")+1)); } } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
DownLoadServlet位于web.controller包
package cn.itcast.web.controller; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URLEncoder; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class DownLoadServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //接收listfiles页面中发来的请求(注意get方式,中文乱码要手动解决) //request.setCharacterEncoding("utf-8");仅针对post提交有效 //得到要下载的文件名 uuid文件名 String full_name=request.getParameter("full_name"); //URL后参数形式提交过来的中文,只能手工转~ full_name=new String(full_name.getBytes("iso8859-1"),"utf-8"); //调用方法,反向根据full_name得出文件在upload父目录下的哪个子目录 //父目录例如/WEB-INF/upload目录 //参数是全球唯一的full_name,形如X-X-X_a.txt //在指定目录下,根据完整文件名的低四次,和次低四位找到文件夹 //返回绝对路径(即完整路径,含全文件名) String parentPath=this.getServletContext().getRealPath("/WEB-INF/upload"); String fullPath=generateChildPath(parentPath,full_name); File file=new File(fullPath); //得到fullPath,先别急着提供下载,先进行健壮性判断 if (!file.exists()) { //如果文件不存在,存消息,跳,返回 request.setAttribute("message", "您来晚了,资源已被销毁"); request.getRequestDispatcher("/message.jsp").forward(request,response); return; } //一路OK,这时才提供下载 //下载之前,要先设置response的头字段, //通知浏览器,以下载文件的方式打开, //注意,解决文件名的中文乱码(用URLEncoder指定utf-8编码) //注意,不能倒着取,因为有可能文件名中也有下划线XX-XX-XX_a_1.txt //得到文件的原始文件名simpleName如 a_1.txt String simpleName=full_name.substring(full_name.indexOf("_")+1); //重点!通知浏览器以下载方式打开下面发送的数据 response.setHeader("content-disposition","attachment;filename="+URLEncoder.encode(simpleName, "utf-8")); InputStream in = null; OutputStream out = null; try { byte[] buf = new byte[1024]; int len = 0; in=new FileInputStream(file); out=response.getOutputStream(); while ((len = in.read(buf)) != -1) { out.write(buf, 0, len); } } catch (IOException e) { e.printStackTrace(); } finally { if (in != null) { try { in.close(); } catch (IOException e) { throw new RuntimeException("in关闭失败!"); } in = null; } if (out != null) { try { out.close(); } catch (IOException e) { throw new RuntimeException("out可以不用管,浏览器销毁response时,自动关流!"); } out = null; } } } private String generateChildPath(String parentPath, String full_name) { //调用方法,反向根据full_name得出文件在upload父目录下的哪个子目录 //父目录例如/WEB-INF/upload目录 //参数是全球唯一的full_name,形如X-X-X_a.txt //在指定目录下,根据完整文件名的低四次,和次低四位找到文件夹 //返回绝对路径(即完整路径,含全文件名) int hashCode=full_name.hashCode(); int dir1=hashCode&15; int dir2=(hashCode>>4)&0xf; return parentPath+File.separator+dir1+File.separator+dir2+File.separator+full_name; } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
UploadServlet3_Copy位于web.controller包
package cn.itcast.web.controller; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; 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.FileUploadException; import org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; /*简单步骤,处理上传文件数据 第1步,首先new 一个未经过配置的工厂实例DiskFileItemFactory 第2步,用提供的工厂创建一个解析上传文件的解析器实例ServletFileUpload!! 第3步,用解析器的parseRequest方法,解析request中提交的数据 每个表单项都封装成一个FileItem对象,加入list集合! 第4步,迭代list集合 第5步,isFormField判断普通字段还是上传字段 如果是上传字段getInputStream,一顿狂写*/ public class CopyOfUploadServlet3 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //下面是详细地处理上传文件过程~注意各类细节问题~ //允许上传的文件后缀名如下: List<String> types=Arrays.asList("jpg","bmp","gif","png","txt","avi","pdf"); try { //第1步,首先new 一个未经配置的工厂实例! DiskFileItemFactory factory =new DiskFileItemFactory(); //细节:解析器缓冲区的大小 factory.setSizeThreshold(1024*1024);//单位是字节,现在是1M //当上传文件过大时,便会用到临时文件夹(应自己确保临时文件的删除) factory.setRepository(new File(this.getServletContext().getRealPath("/temp"))); //第2步,用提供的工厂作参数,创建一个解析上传文件的解析器实例! ServletFileUpload upload=new ServletFileUpload(factory); //正式解析之前,必须先设置单个文件的大小限制!5M upload.setFileSizeMax(1024*1024*5); if(!ServletFileUpload.isMultipartContent(request)){ //细节:如果表单不是upload file,那么就可以按传统方式处理 //只有普通表单,request.setCharacterEncoding("utf-8")才有效果 request.setCharacterEncoding("utf-8"); String value=request.getParameter("username"); System.out.println(value); return; } //细节:在解析request之前,必须先解决上传文件名的中文乱码问题 upload.setHeaderEncoding("UTF-8"); //第3步,用解析器的parseRequest方法,解析request里的数据 //内部会将每个表单项都封装成一个FileItem对象,加入list集合! List<FileItem> list=upload.parseRequest(request); int i=0; for (FileItem item : list) { //先判断是否为简单的表单字段 if (item.isFormField()) { //true表是 a simple form field String fieldName=item.getFieldName(); //注意getString是针对普通字段,取其值的 //getString指定码表,可解决普通表单字段的值的中文乱码问题 //String fieldValue=item.getString("UTF-8"); //或者自己手动解决普通表单字段的值的中文乱码问题 String fieldValue=item.getString(); fieldValue=new String(fieldValue.getBytes("iso8859-1"),"utf-8"); System.out.println("字段名:"+fieldName); System.out.println("username:"+fieldValue); } else { //注意getName是针对上传文件,取其文件名的 // item.getName() 如果是IE6 C:\Documents and Settings\Admin\桌面\a.txt // item.getName() 如果是IE7 a.txt //所以要截取出来真正的上传文件名 String fileName=item.getName(); fileName=fileName.substring(fileName.lastIndexOf("\\")+1); if (fileName==null || "".equals(fileName.trim())) { continue; } //正式处理上传文件之前,先判断文件名和文件大小 //在这儿点号不用转义 String ext=fileName.substring(fileName.lastIndexOf(".")+1); if(!types.contains(ext)){ //如果上传文件的类型不对,存消息,转发,return; request.setAttribute("message", "不支持后缀为."+ext+"的文件上传"); request.getRequestDispatcher("/message.jsp").forward(request,response); return; } //false代表是 an uploaded file //代表当前处理的item里面封装的是上传文件 /*File dest=new File("C:\\upload\\"); //健壮性判断 if (!dest.exists()) { dest.mkdirs(); }*/ InputStream in=null; OutputStream out=null; try { //字节流必须用字节数组 byte[] buf = new byte[1024]; int len = 0; //getInputStream获取关联了上传文件内容的流 in=item.getInputStream(); //关联目的 //out = new FileOutputStream(new File("C:\\upload\\"+fileName)); //文件所在目录,必须保护起来,坚决不能让外界访问 out = new FileOutputStream(new File(this.getServletContext().getRealPath("/WEB-INF/upload")+"\\"+fileName)); //一顿狂写 while ((len = in.read(buf)) != -1) { out.write(buf, 0, len); } }finally { if (in != null) { try { in.close(); } catch (IOException e) { throw new RuntimeException("in关闭失败!"); } in = null; } if (out != null) { try { out.close(); } catch (IOException e) { throw new RuntimeException("out关闭失败!"); } out = null; } //删除临时文件:在程序中处理完上传文件后, //一定要记得调用item.delete()方法,以删除临时文件 //必须是在关闭流之后,finally代码块中,确保删除成功! item.delete(); } } } }catch( FileSizeLimitExceededException e){ //如果上传文件的大小超过限制,存消息,转发,return; request.setAttribute("message", "不支持5M以上大小的文件上传"); request.getRequestDispatcher("/message.jsp").forward(request,response); return; }catch (Exception e) { //出现异常不要紧,记录下来,并给友好提示! e.printStackTrace(); request.setAttribute("message", "XX失败"); request.getRequestDispatcher("/message.jsp").forward(request,response); } //一路无错,就是上传成功 request.setAttribute("message", "上传成功"); request.getRequestDispatcher("/message.jsp").forward(request,response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } /*String sql="C:\\Documents and Settings\\Administrator\\桌面\\a.txt"; System.out.println(sql.substring(sql.lastIndexOf("\\")+1)); 结果是:a.txt sql="b.txt"; System.out.println(sql.substring(sql.lastIndexOf("\\")+1)); 结果是:b.txt*/
substringtest位于junit.test包
package junit.test; public class substringtest { public static void main(String[] args){ String sql="C:\\Documents and Settings\\Administrator\\桌面\\a.txt"; System.out.println(sql.substring(sql.lastIndexOf("\\")+1)); //a.txt sql="b.txt"; System.out.println(sql.substring(sql.lastIndexOf("\\")+1)); //b.txt } }
toLowerCase位于junit.test包
package junit.test; import java.io.File; import org.junit.Test; public class toLowerCase { @Test public void test(){ System.out.println("09809098098jpgAD.JPG".toLowerCase()); //09809098098jpgad.jpg } @Test public void test2(){ System.out.println(File.pathSeparator); //; System.out.println(File.separator); //\ } }
index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'index.jsp' starting page</title> </head> <body> <a href="/day18/upload.jsp">上传文件</a> <a href="/day18/upload2.jsp">动态上传文件</a> <a href="/day18/servlet/ListFilesServlet">列出所有上传文件</a> <form action="${pageContext.request.contextPath }/servlet/UploadServlet3" method="post" > 上传用户:<input type="text" name="username"><br/> <input type="submit" value="普通表单" /> </form> </body> </html>upload.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>上传页面</title> </head> <body> <form action="${pageContext.request.contextPath }/servlet/UploadServlet3" method="post" enctype="multipart/form-data"> 上传用户:<input type="text" name="username"><br/> 上传文件1:<input type="file" name="file_1"><br/> 上传文件2:<input type="file" name="file_2"><br/> <input type="submit" value="开始上传" /> </form> </body> </html>
upload2.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>动态添加file</title> <script type="text/javascript"> function addFile(){ var div=document.getElementById("div_id"); //alert(div); //创建input type为file var file=document.createElement("input"); file.type="file"; //千万要有名字~ file.name="XXX"; //创建input type为button var btn=document.createElement("input"); btn.type="button"; btn.value="删除"; btn.onclick=function del(){ //父是小div,小div的父把小div删除掉~ this.parentNode.parentNode.removeChild(this.parentNode); } //创建个小的div var small_div=document.createElement("div"); //黄鹰抓住了鹞子的脚,两个都扣了环 small_div.appendChild(file); small_div.appendChild(btn); div.appendChild(small_div); } </script> </head> <body> <form action="${pageContext.request.contextPath }/servlet/UploadServlet3"method="post" enctype="multipart/form-data" > <table border="1px"> <tr> <td>用户名:</td> <td><input type="text" name="username"></td> </tr> <tr> <td><input type="button" value="添加上传" onclick="addFile()"></td> <td> <div id="div_id"></div> </td> </tr> <tr> <td><input type="submit" value="开始上传"/></td> </tr> </table> </form> </body> </html>
listfiles.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>显示所有文件列表</title> </head> <body> 注意两点:中文作为url地址后参数时,需要编码 url结合param可以作到这一点!同时会自动添加web应用名<br/> 下载文件有:<br/> <c:forEach var="entry" items="${requestScope.map}"> <c:url var="url" value="/servlet/DownLoadServlet" scope="request"> <c:param name="full_name" value="${entry.key}"></c:param> </c:url> ${entry.value } <a href="${url }">下载</a><br/> </c:forEach> </body> </html>