在JAVAWeb开发中,其中的文件上传和下载功能是非常常用的功能,对于企业公司也会运用到自己的上传下载功能来实现文件传输,而百度云就是一个非常强大的文件传输系统。

       对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器,如果直接使用Servlet获取上传文件的输入流然后再解析里面的请求参数是比较麻烦,所以一般选择采用apache的开源工具common-fileupload这个文件上传组件。这个common-fileupload上传组件的jar包可以去apache官网上面下载。

上传对表单限制

* method="post"
   * enctype="multipart/form-data"
   * 表单中需要添加文件表单项:<input type="file" name="xxx" /><form action="xxx" method="post" enctype="multipart/form-data">
   用户名;<input type="text" name="username"/><br/>
   照 片:<input type="file" name="zhaoPian"/><br/>
   <input type="submit" value="上传"/>
 </form>


上传三步
  相关类:
   * 工厂:DiskFileItemFactory
  * 解析器:ServletFileUpload
  * 表单项:FileItem

  1). 创建工厂:DiskFileItemFactory factory = new DiskFileItemFactory();
  2). 创建解析器:ServletFileUpload sfu = new ServletFileUpload(factory);
  3). 使用解析器来解析request,得到FileItem集合:List<FileItem> fileItemList = sfu.parseRequest(request);
上传的细节:

1. 文件必须保存到WEB-INF下!
  * 目的是不让浏览器直接访问到!
  * 把文件保存到WEB-INF目录下!
2. 文件名称相关问题
 * 有的浏览器上传的文件名是绝对路径,这需要切割!C:\files\baibing.jpg

String filename = fi2.getName();
     int index = filename.lastIndexOf("\\");
     if(index != -1) {
         filename = filename.substring(index+1);
     }

  * 文件名乱码或者普通表单项乱码:request.setCharacterEncoding("utf-8");因为fileupload内部会调用request.getCharacterEncoding();
    > request.setCharacterEncoding("utf-8");//优先级低
    > servletFileUpload.setHeaderEncoding("utf-8");//优先级高
  * 文件同名问题;我们需要为每个文件添加名称前缀,这个前缀要保证不能重复。uuid
    > filename = CommonUtils.uuid() + "_" + filename;
3. 目录打散
  * 不能在一个目录下存放之多文件。
    > 首字符打散:使用文件的首字母做为目录名称,例如:abc.txt,那么我们把文件保存到a目录下。如果a目录这时不存在,那么创建之。
    > 时间打散:使用当前日期做为目录。
    > 哈希打散:
      * 通过文件名称得到int值,即调用hashCode()
      * 它int值转换成16进制0~9, A~F
      * 获取16进制的前两位用来生成目录,目录为二层!例如:1B2C3D4E5F,/1/B/保存文件。
4. 上传文件的大小限制
  * 单个文件大小限制
    > sfu.setFileSizeMax(100*1024):限制单个文件大小为100KB
    > 上面的方法调用,必须在解析开始之前调用!
    > 如果上传的文件超出限制,在parseRequest()方法执行时,会抛出异常!FileUploadBase.FileSizeLimitExceededException
  * 整个请求所有数据大小限制
    > sfu.setSizeMax(1024 * 1024);//限制整个表单大小为1M
    > 这个方法也是必须在parseRequest()方法之前调用
    > 如果上传的文件超出限制,在parseRequest()方法执行时,会抛出异常!FileUploadBase.SizeLimitExceededException
5. 缓存大小与临时目录
  * 缓存大小:超出多大,才向硬盘保存!默认为10KB
  * 临时目录:向硬盘的什么目录保存
  设置缓存大小与临时目录:new DiskFileItemFactory(20*1024, new File("F:/temp"))
 附上    上传相关代码:

public class Upload3Servlet extends HttpServlet {
	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html;charset=utf-8");
		
		/*
		 * 上传三步
		 */
		// 工厂
		DiskFileItemFactory factory = new DiskFileItemFactory(20*1024, new File("F:/f/temp"));
		// 解析器
		ServletFileUpload sfu = new ServletFileUpload(factory);
//		sfu.setFileSizeMax(100 * 1024);//限制单个文件大小为100K
//		sfu.setSizeMax(1024 * 1024);//限制整个表单大小为1M
		
		// 解析,得到List
		try {
			List<FileItem> list = sfu.parseRequest(request);
			FileItem fi = list.get(1);
			
			
			/*
			 * 1. 得到文件保存的路径
			 */
			String root = this.getServletContext().getRealPath("/WEB-INF/files/");
			/*
			 * 2. 生成二层目录
			 *   1). 得到文件名称
			 *   2). 得到hashCode
			 *   3). 转发成16进制
			 *   4). 获取前二个字符用来生成目录
			 */
			String filename = fi.getName();//获取上传的文件名称
			/*
			 * 处理文件名的绝对路径问题
			 */
			int index = filename.lastIndexOf("\\");
			if(index != -1) {
				filename = filename.substring(index+1);
			}
			/*
			 * 给文件名称添加uuid前缀,处理文件同名问题
			 */
			String savename = CommonUtils.uuid() + "_" + filename;
			
			/*
			 * 1. 得到hashCode
			 */
			int hCode = filename.hashCode();
			String hex = Integer.toHexString(hCode);
			
			/*
			 * 2. 获取hex的前两个字母,与root连接在一起,生成一个完整的路径
			 */
			File dirFile = new File(root, hex.charAt(0) + "/" + hex.charAt(1));
			
			/*
			 * 3. 创建目录链
			 */
			dirFile.mkdirs();
			
			/*
			 * 4. 创建目录文件
			 */
			File destFile = new File(dirFile, savename);
			
			/*
			 * 5. 保存
			 */
			fi.write(destFile);
			
		} catch (FileUploadException e) {
			if(e instanceof FileUploadBase.FileSizeLimitExceededException) {
				request.setAttribute("msg", "您上传的文件超出了100KB!");
				request.getRequestDispatcher("/form3.jsp").forward(request, response);
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

       我们要将Web应用系统中的文件资源提供给用户进行下载,首先我们要有一个页面列出上传文件目录下的所有文件,当用户点击文件下载超链接时就进行下载操作,编写一个Servlet,用于列出Web应用系统中所有下载文件。

下载的要求
  * 两个头一个流!
    > Content-Type:你传递给客户端的文件是什么MIME类型,例如:image/pjpeg
      * 通过文件名称调用ServletContext的getMimeType()方法,得到MIME类型!
    > Content-Disposition:它的默认值为inline,表示在浏览器窗口中打开!attachment;filename=xxx
      * 在filename=后面跟随的是显示在下载框中的文件名称!
    > 流:要下载的文件数据!
      * 自己new一个输入流即可!

通用方案:filename = new String(filename.getBytes("GBK"), "ISO-8859-1");


	public static String filenameEncoding(String filename, HttpServletRequest request) throws IOException {
		String agent = request.getHeader("User-Agent"); //获取浏览器
		if (agent.contains("Firefox")) {
			BASE64Encoder base64Encoder = new BASE64Encoder();
			filename = "=?utf-8?B?"
					+ base64Encoder.encode(filename.getBytes("utf-8"))
					+ "?=";
		} else if(agent.contains("MSIE")) {
			filename = URLEncoder.encode(filename, "utf-8");
		} else {
			filename = URLEncoder.encode(filename, "utf-8");
		}
		return filename;
	}

//下载功能实现
public class Download1Servlet extends HttpServlet {
	@Override
	public void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		/*
		 * 两个头一个流
		 * 1. Content-Type
		 * 2. Content-Disposition
		 * 3. 流:下载文件的数据
		 */
		String filename = "F:/流光飞舞.mp3";
		
		// 为了使下载框中显示中文文件名称不出乱码!
//		String framename = new String("流光%飞舞.mp3".getBytes("GBK"), "ISO-8859-1");
		String framename = filenameEncoding("流光%飞舞.mp3", req);
		
		String contentType = this.getServletContext()
				.getMimeType(filename);//通过文件名称获取MIME类型
		String contentDisposition = "attachment;filename=" + framename;
		// 一个流
		FileInputStream input = new FileInputStream(filename);
		
		//设置头
		resp.setHeader("Content-Type", contentType);
		resp.setHeader("Content-Disposition", contentDisposition);
		
		// 获取绑定了响应端的流
		ServletOutputStream output = resp.getOutputStream();
		
		IOUtils.copy(input, output);//把输入流中的数据写入到输出流中。
		
		input.close();
	}
	
	// 用来对下载的文件名称进行编码的!
	public static String filenameEncoding(String filename, HttpServletRequest request) throws IOException {
		String agent = request.getHeader("User-Agent"); //获取浏览器
		if (agent.contains("Firefox")) {
			BASE64Encoder base64Encoder = new BASE64Encoder();
			filename = "=?utf-8?B?"
					+ base64Encoder.encode(filename.getBytes("utf-8"))
					+ "?=";
		} else if(agent.contains("MSIE")) {
			filename = URLEncoder.encode(filename, "utf-8");
		} else {
			filename = URLEncoder.encode(filename, "utf-8");
		}
		return filename;
	}
}