说明位于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"));



UploadServlet1位于web.controller包

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*/



UploadServlet3

位于

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 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*/






ListFilesServlet位于

web.controller

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.οnclick=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="添加上传" οnclick="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>