黑马程序员技术交流社区

标题: 文件上传与下载详细解析与实现 [打印本页]

作者: 路文龙    时间: 2015-3-8 22:11
标题: 文件上传与下载详细解析与实现
实现web开发中的文件上传功能,需完成如下二步操作:
在web页面中添加上传输入项
在servlet中读取上传文件的数据,并保存到本地硬盘中。
如何在web页面中添加上传输入项?
<input type=“file”>标签用于在web页面中添加文件上传输入项,设置文件上传输入项时须注意:
1、必须要设置input输入项的name属性,否则浏览器将不会发送上传文件的数据。
2、必须把form的enctype属值设为multipart/form-data.设置该值后,浏览器在上传文件时,将把文件数据附带在http请求消息体中,并使用MIME协议对上传的文件进行描述,以方便接收方对上传数据进行解析和处理。
upload.jsp:
  1. <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

  2. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
  3. <html>
  4.   <head>
  5.     <title>My JSP 'upload.jsp' starting page</title>
  6.   </head>
  7.   <body>
  8.     <form action="${pageContext.request.contextPath }/servlet/UploadServlet3" <font color="Red">enctype="multipart/form-data" method="post"</font>>
  9.             上传用户:<input type="text" name="username"><br/>
  10.             上传文件1:<input type="file" name="file1"><br/>
  11.             上传文件2:<input type="file" name="file2"><br/>
  12.             <input type="submit" value="上传">
  13.     </form>
  14.   </body>
  15. </html>
复制代码
-->见2楼

作者: 路文龙    时间: 2015-3-8 22:11
本帖最后由 路文龙 于 2015-3-8 22:00 编辑

jsp页面:

请求信息:

如何在Servlet中读取文件上传数据,并保存到本地硬盘中?
Request对象提供了一个getInputStream方法,通过这个方法可以读取到客户端提交过来的数据。但由于用户可能会同时上传多个文件,在servlet端编程直接读取上传数据,并分别解析出相应的文件数据是一项非常麻烦的工作,示例。

为方便用户处理文件上传数据,Apache 开源组织提供了一个用来处理表单文件上传的一个开源组件( Commons-fileupload ),该组件性能优异,并且其API使用极其简单,可以让开发人员轻松实现web文件上传功能,因此在web开发中实现文件上传功能,通常使用Commons-fileupload组件实现。
fileupload组件工作流程:

使用Commons-fileupload实现:
UploadServlet2.java
  1. package cn.itcast.web.servlet;

  2. import java.io.FileOutputStream;
  3. import java.io.IOException;
  4. import java.io.InputStream;
  5. import java.util.List;

  6. import javax.servlet.ServletException;
  7. import javax.servlet.http.HttpServlet;
  8. import javax.servlet.http.HttpServletRequest;
  9. import javax.servlet.http.HttpServletResponse;

  10. import org.apache.commons.fileupload.FileItem;
  11. import org.apache.commons.fileupload.disk.DiskFileItemFactory;
  12. import org.apache.commons.fileupload.servlet.ServletFileUpload;

  13. //处理上传数据
  14. public class UploadServlet2 extends HttpServlet {

  15.         public void doGet(HttpServletRequest request, HttpServletResponse response)
  16.                         throws ServletException, IOException {
  17.                
  18.                 try{
  19.                         DiskFileItemFactory factory = new DiskFileItemFactory();
  20.                         ServletFileUpload upload = new ServletFileUpload(factory);
  21.                         
  22.                         List<FileItem> list = upload.parseRequest(request);
  23.                         for(FileItem item : list){
  24.                                 if(item.isFormField()){
  25.                                         //为普通输入项
  26.                                         String inputName = item.getFieldName();
  27.                                         String inputValue = item.getString();
  28.                                         System.out.println(inputName + "="  + inputValue);
  29.                                 }else{
  30.                                         //代表当前处理的item里面封装的是上传文件
  31.                                         //C:\Documents and Settings\ThinkPad\桌面\a.txt    a.txt
  32.                                         String filename = item.getName().substring(item.getName().lastIndexOf("\\")+1);  
  33.                                         InputStream in = item.getInputStream();
  34.                                         int len = 0;
  35.                                         byte buffer[] = new byte[1024];
  36.                                         FileOutputStream out = new FileOutputStream("c:\\" + filename);
  37.                                         while((len=in.read(buffer))>0){
  38.                                                 out.write(buffer, 0, len);
  39.                                         }
  40.                                         in.close();
  41.                                         out.close();
  42.                                 }
  43.                         }
  44.                 }catch (Exception e) {
  45.                         throw new RuntimeException(e);
  46.                 }
  47.         }

  48.         public void doPost(HttpServletRequest request, HttpServletResponse response)
  49.                         throws ServletException, IOException {
  50.                 doGet(request, response);
  51.         }

  52. }
复制代码
核心API—DiskFileItemFactory:
DiskFileItemFactory 是创建 FileItem 对象的工厂,这个工厂类常用方法:
1、public void setSizeThreshold(int sizeThreshold) :
设置内存缓冲区的大小,默认值为10K。当上传文件大于缓冲区大小时, fileupload组件将使用临时文件缓存上传文件。
2、public void setRepository(java.io.File repository):
指定临时文件目录,默认值为System.getProperty("java.io.tmpdir").
3、public DiskFileItemFactory(int sizeThreshold, java.io.File repository):
构造函数

核心API—ServletFileUpload
ServletFileUpload 负责处理上传的文件数据,并将表单中每个输入项封装成一个 FileItem 对象中。常用方法有:
1 boolean isMultipartContent(HttpServletRequest request)
判断上传表单是否为multipart/form-data类型
2 List parseRequest(HttpServletRequest request)
解析request对象,并把表单中的每一个输入项包装成一个fileItem 对象,并返回一个保存了所有FileItem的list集合。
3 setFileSizeMax(long fileSizeMax)
设置上传文件的最大值
4 setSizeMax(long sizeMax)
设置上传文件总量的最大值
5 setHeaderEncoding(java.lang.String encoding)
设置编码格式




作者: 路文龙    时间: 2015-3-8 22:11
上传常见问题:
1.上传文件的中文乱码
    1.1 解决文件的乱码
        ServletFileUpload.setHeaderEncoding("UTF-8")
    1.2 解决普通输入项的乱码(注意,表单类型为multipart/form-data的时候,设置request的编码是无效的)
        FileItem.setString("UTF-8");  //解决乱码

2.在处理表单之前,要记得调用:
    ServletFileUpload.isMultipartContent方法判断提交表单的类型,如果该方法返回true,则按上传方式处理,否则按照传统方式处理表单即可。


3.设置解析器缓冲区的大小,以及临时文件的删除
    设置解析器缓冲区的大小  DiskFileItemFactory.setSizeThreshold(1024*1024);
    临时文件的删除:在程序中处理完上传文件后,一定要记得调用item.delete()方法,以删除临时文件

4.在做上传系统时,千万要注意上传文件的保存目录,这个上传文件的保存目录绝对不能让外界直接访问到。

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);
                }
            });

11、在web页面中添加动态上传输入项
UploadServlet3.java 解决了上述问题
  1. package cn.itcast.web.servlet;

  2. import java.io.File;
  3. import java.io.FileOutputStream;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.util.Arrays;
  7. import java.util.List;
  8. import java.util.UUID;

  9. import javax.servlet.ServletException;
  10. import javax.servlet.http.HttpServlet;
  11. import javax.servlet.http.HttpServletRequest;
  12. import javax.servlet.http.HttpServletResponse;

  13. import org.apache.commons.fileupload.FileItem;
  14. import org.apache.commons.fileupload.FileUploadBase;
  15. import org.apache.commons.fileupload.ProgressListener;
  16. import org.apache.commons.fileupload.disk.DiskFileItemFactory;
  17. import org.apache.commons.fileupload.servlet.ServletFileUpload;

  18. public class UploadServlet3 extends HttpServlet {

  19. public void doGet(HttpServletRequest request, HttpServletResponse response)
  20. throws ServletException, IOException {

  21. List types = Arrays.asList("jpg","gif","avi","txt");

  22. try{
  23. DiskFileItemFactory factory = new DiskFileItemFactory(); //10k
  24. factory.setSizeThreshold(1024*1024);
  25. factory.setRepository(new File(this.getServletContext().getRealPath("/temp")));

  26. ServletFileUpload upload = new ServletFileUpload(factory);
  27. upload.setProgressListener(new ProgressListener(){
  28. public void update(long pBytesRead, long pContentLength, int pItems) {
  29. System.out.println("当前已解析:" + pBytesRead);
  30. }
  31. });

  32. upload.setFileSizeMax(1024*1024*5);
  33. if(!upload.isMultipartContent(request)){
  34. //按照传统方式获取表单数据
  35. request.getParameter("username");
  36. return;
  37. }
  38. upload.setHeaderEncoding("UTF-8");
  39. List<FileItem> list = upload.parseRequest(request);

  40. for(FileItem item : list){
  41. if(item.isFormField()){
  42. //为普通输入项
  43. String inputName = item.getFieldName();
  44. String inputValue = item.getString("UTF-8");
  45. //inputValue = new String(inputValue.getBytes("iso8859-1"),"UTF-8");
  46. System.out.println(inputName + "=" + inputValue);
  47. }else{
  48. String filename = item.getName().substring(item.getName().lastIndexOf("\\")+1); //""
  49. if(filename==null || filename.trim().equals("")){
  50. continue;
  51. }

  52. /*String ext = filename.substring(filename.lastIndexOf(".")+1);
  53. if(!types.contains(ext)){
  54. request.setAttribute("message", "本系统不支持" + ext + "这种类型");
  55. request.getRequestDispatcher("/message.jsp").forward(request, response);
  56. return;
  57. }*/
  58. InputStream in = item.getInputStream();
  59. int len = 0;
  60. byte buffer[] = new byte[1024];
  61. String saveFileName = generateFileName(filename);
  62. String savepath = generateSavePath(this.getServletContext().getRealPath("/WEB-INF/upload"),saveFileName);
  63. FileOutputStream out = new FileOutputStream(savepath + File.separator + saveFileName);
  64. while((len=in.read(buffer))>0){
  65. out.write(buffer, 0, len);
  66. }
  67. in.close();
  68. out.close();
  69. item.delete(); //删除临时文件
  70. }
  71. }
  72. }catch (FileUploadBase.FileSizeLimitExceededException e) {
  73. request.setAttribute("message", "文件大小不能超过5m");
  74. request.getRequestDispatcher("/message.jsp").forward(request, response);
  75. return;
  76. }catch (Exception e) {
  77. throw new RuntimeException(e);
  78. }
  79. request.setAttribute("message", "上传成功!!");
  80. request.getRequestDispatcher("/message.jsp").forward(request, response);
  81. }

  82. //
  83. public String generateSavePath(String path,String filename){
  84. int hashcode = filename.hashCode(); //121221
  85. int dir1 = hashcode&15;
  86. int dir2 = (hashcode>>4)&0xf;

  87. String savepath = path + File.separator + dir1 + File.separator + dir2;
  88. File file = new File(savepath);
  89. if(!file.exists()){
  90. file.mkdirs();
  91. }
  92. return savepath;
  93. }

  94. public String generateFileName(String filename){
  95. //83434-83u483-934934
  96. return UUID.randomUUID().toString() + "_" + filename;
  97. }

  98. public void doPost(HttpServletRequest request, HttpServletResponse response)
  99. throws ServletException, IOException {
  100. doGet(request, response);
  101. }

  102. }
复制代码




作者: 路文龙    时间: 2015-3-8 22:11
文件下载
Web应用中实现文件下载的两种方式
超链接直接指向下载资源
程序实现下载需设置两个响应头:
设置Content-Type 的值为:application/x-msdownload。Web 服务器需要告诉浏览器其所输出的内容的类型不是普通的文本文件或 HTML 文件,而是一个要保存到本地的下载文件。
1、Web 服务器希望浏览器不直接处理相应的实体内容,而是由用户选择将相应的实体内容保存到一个文件中,这需要设置 Content-Disposition 报头。该报头指定了接收程序处理数据内容的方式,在 HTTP 应用中只有 attachment 是标准方式,attachment 表示要求用户干预。在 attachment 后面还可以指定 filename 参数,该参数是服务器建议浏览器将实体内容保存到文件中的文件名称。在设置 Content-Dispostion 之前一定要指定 Content-Type.
2、因为要下载的文件可以是各种类型的文件,所以要将文件传送给客户端,其相应内容应该被当做二进制来处理,所以应该调用response.getOutputStream();方法返回 ServeltOutputStream 对象来向客户端写入文件内容。     
3、遍历上传目录下的所有文件显示给用户,并允许用户完成下载。
ListFileServlet.java
  1. package cn.itcast.web.servlet;

  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.util.HashMap;
  5. import java.util.Map;

  6. import javax.servlet.ServletException;
  7. import javax.servlet.http.HttpServlet;
  8. import javax.servlet.http.HttpServletRequest;
  9. import javax.servlet.http.HttpServletResponse;
  10. //列出网站所有文件
  11. public class ListFileServlet extends HttpServlet {

  12.         public void doGet(HttpServletRequest request, HttpServletResponse response)
  13.                         throws ServletException, IOException {

  14.                 String path = this.getServletContext().getRealPath("/WEB-INF/upload");
  15.                 Map map = new HashMap();
  16.                 listfile(new File(path),map);
  17.                
  18.                 request.setAttribute("map", map);
  19.                 request.getRequestDispatcher("/listfile.jsp").forward(request, response);
  20.         }
  21.         
  22.         //如何保存迭归出来的资源
  23.         public void listfile(File file,Map map){
  24.                 if(!file.isFile()){
  25.                         File children[] = file.listFiles();
  26.                         for(File f : children){
  27.                                 listfile(f,map);
  28.                         }
  29.                 }else{
  30.                         String filename = file.getName().substring(file.getName().indexOf("_")+1);              //8347824284-343-343_a_b.txt
  31.                         map.put(file.getName(),filename);   //<a href="/servlet?filename=文件在服务器的名称">文件的原始文件名</a>
  32.                 }
  33.         }

  34.         public void doPost(HttpServletRequest request, HttpServletResponse response)
  35.                         throws ServletException, IOException {
  36.                 doGet(request, response);
  37.         }

  38. }
复制代码
listfile.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>文件列表</title>
  7.   </head>
  8.   
  9.   <body>
  10.    
  11.     下载文件有:<br/>
  12.     <c:forEach var="entry" items="${requestScope.map}">   
  13.             <c:url var="url" value="/servlet/DownLoadServlet">
  14.                     <c:param name="filename" value="${entry.key}"></c:param>
  15.             </c:url>
  16.             ${entry.value }        <a href="${url }">下载</a><br/>
  17.     </c:forEach>
  18.   </body>
  19. </html>
复制代码








欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2