文件上传下载案例:
首先是此案例工程的目录结构:
处理上传: FileUploadServlet.java
1 package fnz.fileUploadTest; 2 3 import java.io.File; 4 import java.io.IOException; 5 import java.io.InputStream; 6 import java.io.PrintWriter; 7 import java.text.SimpleDateFormat; 8 import java.util.Date; 9 import java.util.HashMap; 10 import java.util.List; 11 import java.util.Map; 12 13 import javax.servlet.ServletException; 14 import javax.servlet.http.HttpServlet; 15 import javax.servlet.http.HttpServletRequest; 16 import javax.servlet.http.HttpServletResponse; 17 18 import org.apache.commons.fileupload.FileItem; 19 import org.apache.commons.fileupload.FileUploadException; 20 import org.apache.commons.fileupload.disk.DiskFileItemFactory; 21 import org.apache.commons.fileupload.servlet.ServletFileUpload; 22 23 public class FileUploadServlet extends HttpServlet { 24 25 public void doGet(HttpServletRequest request, HttpServletResponse response) 26 throws ServletException, IOException { 27 try { 28 //创建 FileItem 对象的工厂 29 DiskFileItemFactory fileItemFactory = new DiskFileItemFactory(); 30 //负责处理上传的文件数据,并将表单中每个输入项封装成一个 FileItem 对象中 31 ServletFileUpload sfu = new ServletFileUpload(fileItemFactory); 32 //设置单个文件最大10M 33 sfu.setFileSizeMax(10*1024*1024); 34 //设置总文件最大100M 35 sfu.setSizeMax(100*1024*1024); 36 // 设置编码,相当于:request.setCharacterEncoding("UTF-8"); 37 sfu.setHeaderEncoding("UTF-8"); 38 //判断上传表单是否为multipart/form-data类型 39 if(sfu.isMultipartContent(request)){ 40 //解析request对象,并把表单中的每一个输入项包装成一个fileItem 对象,并返回一个保存了所有FileItem的list集合 41 List<FileItem> list = sfu.parseRequest(request); 42 for (FileItem fileItem : list) { 43 //判断:是否是普通表单字段 44 if(fileItem.isFormField()){ 45 String fieldName = fileItem.getFieldName();//name 46 String value = fileItem.getString();//value 47 System.out.println(fieldName+" "+value); 48 } 49 //否则为上传文件 50 else{ 51 //String fieldName = fileItem.getFieldName(); // 表单元素名称 name 52 String name = fileItem.getName(); // 文件名 53 //String content = fileItem.getString(); // 表单元素名称, 对应的数据 value 54 //String type = fileItem.getContentType(); // 文件类型 55 //InputStream in = fileItem.getInputStream(); // 上传文件流 56 // 获取项目文件夹upload对应服务器上的upload路径 57 String path = getServletContext().getRealPath("/upload"); 58 59 //用当前时间+文件名 来命名上传后的文件名 60 SimpleDateFormat time = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_S"); 61 String str = time.format(new Date()); 62 // b. 与文件名拼接 63 name = str +"#"+ name; 64 65 //创建一个文件 66 File file = new File(path,name); 67 //上传 68 fileItem.write(file); 69 //删除系统产生的临时文件 70 fileItem.delete(); 71 System.out.println("OK"); 72 } 73 } 74 } 75 76 } catch (Exception e) { 77 throw new RuntimeException(e); 78 } 79 80 81 //将文件放入request域中 82 Map<String,String> fileNames = new HashMap<String,String>(); 83 String downFolderPath = getServletContext().getRealPath("/upload"); 84 File file = new File(downFolderPath); 85 String[] files = file.list(); 86 if(files!=null){ 87 for (String fileItem : files) { 88 String[] splitNames = fileItem.split("#"); 89 fileNames.put(fileItem, splitNames[1]!=null?splitNames[1]:fileItem); 90 } 91 } 92 request.setAttribute("fileNames",fileNames); 93 request.getRequestDispatcher("/index.jsp").forward(request, response); 94 } 95 96 public void doPost(HttpServletRequest request, HttpServletResponse response) 97 throws ServletException, IOException { 98 this.doGet(request, response); 99 } 100 101 }
处理下载:FileDownLoadServlet.java
1 package fnz.fileUploadTest; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.IOException; 6 import java.io.InputStream; 7 import java.io.OutputStream; 8 import java.io.PrintWriter; 9 import java.net.URLEncoder; 10 11 import javax.servlet.ServletException; 12 import javax.servlet.http.HttpServlet; 13 import javax.servlet.http.HttpServletRequest; 14 import javax.servlet.http.HttpServletResponse; 15 16 public class FileDownLoadServlet extends HttpServlet { 17 18 public void doGet(HttpServletRequest request, HttpServletResponse response) 19 throws ServletException, IOException { 20 21 // 获取用户下载的文件名称(url地址后追加数据,get) 22 String fileName = request.getParameter("downFileName"); 23 fileName = new String(fileName.getBytes("ISO8859-1"),"UTF-8"); 24 25 // 先获取上传目录路径 26 String basePath = getServletContext().getRealPath("/upload"); 27 // 获取一个文件流 28 InputStream in = new FileInputStream(new File(basePath,fileName)); 29 30 // 如果文件名是中文,需要进行url编码 31 fileName = URLEncoder.encode(fileName, "UTF-8"); 32 // 设置下载的响应头 33 response.setContentType("application/x-msdownload"); 34 response.setHeader("content-disposition", "attachment;fileName=" + fileName); 35 36 // 获取response字节流 37 OutputStream out = response.getOutputStream(); 38 byte[] b = new byte[1024]; 39 int len = -1; 40 while ((len = in.read(b)) != -1){ 41 out.write(b, 0, len); 42 } 43 // 关闭 44 out.close(); 45 in.close(); 46 } 47 48 public void doPost(HttpServletRequest request, HttpServletResponse response) 49 throws ServletException, IOException { 50 this.doGet(request, response); 51 } 52 53 }
前台操作和显示:index.jsp
1 <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> 2 <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 3 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 4 <html> 5 <head> 6 <title>FileUpload</title> 7 </head> 8 9 <body> 10 <form action="${pageContext.request.contextPath}/FileUploadServlet" method="post" enctype="multipart/form-data"> 11 <input type="file" name="file1"><br/> 12 <input type="submit" value="提交"> 13 </form> 14 <hr> 15 已上传的文件,点击下载<br/> 16 <c:if test="${not empty requestScope.fileNames }"> 17 <c:forEach var="item" items="${requestScope.fileNames}" > 18 <c:url var="url" value="FileDownLoadServlet"> 19 <c:param name="downFileName" value="${item.key}"></c:param> 20 </c:url> 21 <a href="${url }">${item.value }</a><br/> 22 </c:forEach> 23 </c:if> 24 </body> 25 </html>
文件上传和下载注意事项及解释:
文件上传:
<input type="file">标签用于在web页面中添加文件上传输入项,设置文件上传输入项时须注意:
1、必须要设置input输入项的name属性,否则浏览器将不会发送上传文件的数据。
2、必须把form的enctype属性值设为multipart/form-data.method
提交方式设为post方式。设置该值后,浏览器在上传文件时,将把文件数据附带在http请求消息体中,并使用MIME协议对上传的文件进行描述,以方便接收方对上传数据进行解析和处理。
使用Commons-fileupload组件实现文件上传
需要导入该组件相应的支撑jar包:Commons-fileupload和commons-io。commons-io 不属于文件上传组件的开发jar文件,但Commons-fileupload 组件从1.1 版本开始,它工作时需要commons-io包的支持。
核心API—DiskFileItemFactory
DiskFileItemFactory 是创建 FileItem 对象的工厂,这个工厂类常用方法:
public DiskFileItemFactory(int sizeThreshold, java.io.File repository)
构造函数
public void setSizeThreshold(int sizeThreshold)
设置内存缓冲区的大小,默认值为10K。当上传文件大于缓冲区大小时, fileupload组件将使用临时文件缓存上传文件。
public void setRepository(java.io.File repository)
指定临时文件目录,默认值为System.getProperty("java.io.tmpdir")
核心API—ServletFileUpload
ServletFileUpload 负责处理上传的文件数据,并将表单中每个输入项封装成一个 FileItem 对象中。常用方法有:
boolean isMultipartContent(HttpServletRequest request)
判断上传表单是否为multipart/form-data类型
List parseRequest(HttpServletRequest request)
解析request对象,并把表单中的每一个输入项包装成一个fileItem 对象,并返回一个保存了所有FileItem的list集合。
setFileSizeMax(long fileSizeMax)
设置上传文件的最大值
setSizeMax(long sizeMax)
设置上传文件总量的最大值
setHeaderEncoding(java.lang.String encoding)
设置编码格式
setProgressListener(ProgressListener pListener)
上传文件的处理细节
中文文件乱码问题
文件名中文乱码问题,可调用ServletUpLoader的setHeaderEncoding方法,或者设置request的setCharacterEncoding属性
临时文件的删除问题
由于文件大小超出DiskFileItemFactory.setSizeThreshold方法设置的内存缓冲区的大小时,Commons-fileupload组件将使用临时文件保存上传数据,因此在程序结束时,务必调用FileItem.delete方法删除临时文件。
Delete方法的调用必须位于流关闭之后,否则会出现文件占用,而导致删除失败的情况。
文件存放位置
为保证服务器安全,上传文件应保存在应用程序的WEB-INF目录下,或者不受WEB服务器管理的目录。
为防止多用户上传相同文件名的文件,而导致文件覆盖的情况发生,文件上传程序应保证上传文件具有唯一文件名。
为防止单个目录下文件过多,影响文件读写速度,处理上传文件的程序应根据可能的文件上传总量,选择合适的目录结构生成算法,将上传文件分散存储。
ProgressListener显示上传进度
ProgressListener progressListener = new ProgressListener() {
public void update(long pBytesRead, long pContentLength, int pItems) {
System.out.println("到现在为止, " + pBytesRead + " 字节已上传,总大小为 "
+ pContentLength);
}
};
upload.setProgressListener(progressListener);
以KB为单位显示上传进度
long temp = -1; //temp注意设置为类变量
long ctemp = pBytesRead /1024;
if (mBytes == ctemp)
return;
temp = mBytes;
文件下载
Web应用中实现文件下载的两种方式
超链接直接指向下载资源
程序实现下载需设置两个响应头:
设置Content-Type 的值为:application/x-msdownload。Web 服务器需要告诉浏览器其所输出的内容的类型不是普通的文本文件或 HTML 文件,而是一个要保存到本地的下载文件。
Web 服务器希望浏览器不直接处理相应的实体内容,而是由用户选择将相应的实体内容保存到一个文件中,这需要设置 Content-Disposition 报头。该报头指定了接收程序处理数据内容的方式,在 HTTP 应用中只有 attachment 是标准方式,attachment 表示要求用户干预。在 attachment 后面还可以指定 filename 参数,该参数是服务器建议浏览器将实体内容保存到文件中的文件名称。在设置 Content-Dispostion 之前一定要指定 Content-Type
因为要下载的文件可以是各种类型的文件,所以要将文件传送给客户端,其相应内容应该被当做二进制来处理,所以应该调用response.getOutputStream()方法返回 ServeltOutputStream 对象来向客户端写入文件内容。
byte[] b = new byte[1024];
int len = -1;
while ((len = in.read(b)) != -1){
out.write(b, 0, len);
}