JavaWeb 文件上传与下载

来源:互联网 发布:网络统考app 编辑:程序博客网 时间:2024/06/05 18:35

一、文件上传

       实现 Web 开发中的文件上传功能,两个操作:在 Web 页面添加上传输入项,在 Servlet 中读取上传文件的数据并保存在本地硬盘中。

     1、Web 端上传文件。在 Web 页面中添加上传输入项:<input type="file"> 设置文件上传输入项时应注意:(1) 必须设置 input 输入项的 name 属性,否则浏览器将不会发送上传文件的数据。(2) 必须把 form 的 enctype 属性设为 multipart/form-data,设置该值后,浏览器在上传文件时,将把文件数据附带在 http 请求消息体中,并使用 MIME 协议对上传文件进行描述,以方便接收方对上传数据进行解析和处理。(3) 表单提交的方式要是 post

         2、服务器端获取文件。如果提交表单的类型为 multipart/form-data 时,就不能采用传统方式获取数据。因为当表单类型为 multipart/form-data 时,浏览器会将数据以 MIME 协议的形式进行描述。如果想在服务器端获取数据,那么我们必须采用获取请求消息输入流的方式来获取数据。

         3、Apache-Commons-fileupload。为了方便用户处理上传数据,Apache 提供了一个用来处理表单文件上传的开源组建。使用 Commons-fileupload 需要 Commons-io 包的支持。

         4、fileuplpad 组建工作流程

              (1)客户端将数据封装在 request 对象中。

              (2)服务器端获取到 request 对象。

              (3)创建解析器工厂 DiskFileItemFactory 。

              (4)创建解析器,将解析器工厂放入解析器构造函数中。之后解析器会对 request 进行解析。

              (5)解析器会将每个表单项封装为各自对应的 FileItem。

              (6)判断代表每个表单项的 FileItem 是否为普通表单项 isFormField,返回 true 为普通表单项。

              (7)如果是普通表单项,通过 getFieldName 获取表单项名,getString 获得表单项值。

              (8)如果 isFormField 返回 false 那么是用户要上传的数据,可以通过 getInputStream 获取上传文件的数据。通过getName 可以获取上传的文件名。

                

 

            5、DiskFileItemFactory。DiskFileItemFactory 是创建 FileItem 对象的工厂,常用的方法有:

          setSizeThreshold(int sizeThreshold,File repository ) 设置内存缓冲区的大小,默认值为 10k 当上传文件大于缓冲区大小时,fileupload 组建将使用临时文件缓存上传文件。还可以设置临时文件目录。

          setRepository(File repository) 指定临时文件目录默认值为 System.getProperty("java.io.tmpdir")

       6、ServletFileUpload。ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个 FileItem 对象中,常用方法有:

          isMultipartContent(HttpServletRequest request) 判断上传表单是否为 multipart/form-data。

          parseRequest(HttpServletRequest request) 解析 request 对象,并把表单中的每一个输入项包装成一个 fileItem 对象,并反悔一个保存了所有 FileItem 的 List 集合。

          setFileSizeMax(long fileSizeMax) 设置上传文件的最大值。

          setSizeMax(long sizeMax) 设置上传文件总量的最大值。

          setHeaderEncoding(String encoding) 设置编码方式。

          setProgressListener(ProgressListener pListener) 指定一个监听器。

 

[java] view plain copy
  1. package upload;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileOutputStream;  
  5. import java.io.IOException;  
  6. import java.io.InputStream;  
  7. import java.util.List;  
  8. import javax.servlet.ServletException;  
  9. import javax.servlet.http.HttpServlet;  
  10. import javax.servlet.http.HttpServletRequest;  
  11. import javax.servlet.http.HttpServletResponse;  
  12. import org.apache.commons.fileupload.FileItem;  
  13. import org.apache.commons.fileupload.FileUploadException;  
  14. import org.apache.commons.fileupload.disk.DiskFileItemFactory;  
  15. import org.apache.commons.fileupload.servlet.ServletFileUpload;  
  16.   
  17. @SuppressWarnings("serial")  
  18. public class UploadServlet extends HttpServlet {  
  19.   
  20.     @SuppressWarnings("unchecked")  
  21.     public void doGet(HttpServletRequest request, HttpServletResponse response)  
  22.             throws ServletException, IOException {  
  23.         DiskFileItemFactory factory = new DiskFileItemFactory();  
  24.         factory.setRepository(new File("d:/temp"));  
  25.         ServletFileUpload upload = new ServletFileUpload(factory);  
  26.         if (ServletFileUpload.isMultipartContent(request)) {  
  27.             try {  
  28.                 List<FileItem> list = upload.parseRequest(request);  
  29.                 for (FileItem item : list) {  
  30.                     if (item.isFormField()) {  
  31.                         System.out.println("普通字段:"  
  32.                                 + item.getString());  
  33.                     } else {  
  34.                         InputStream in = item.getInputStream();  
  35.                         String fileName = item.getName();  
  36.                         FileOutputStream fos = new FileOutputStream(this.getServletContext().getRealPath("/WEB-INF/upload")+"/" + fileName);  
  37.                         int len = 0;  
  38.                         byte[] b = new byte[1024];  
  39.                         while ((len = in.read(b)) != -1) {  
  40.                             fos.write(b, 0, len);  
  41.                         }  
  42.                         fos.close();  
  43.                     }  
  44.                 }  
  45.             } catch (FileUploadException e) {  
  46.                 request.setAttribute("message""上传失败。。");  
  47.                 request.getRequestDispatcher("/message").forward(request,  
  48.                         response);  
  49.             }  
  50.         }  
  51.     }  
  52.   
  53.     public void doPost(HttpServletRequest request, HttpServletResponse response)  
  54.             throws ServletException, IOException {  
  55.         doGet(request, response);  
  56.     }  
  57.   
  58. }  

FileItem.getFieldName() 获取的是普通表单项的表单项名。

FileItem.getString(encod) 获取的是普通表单项的表单项值。

FileItem.getName() 获取的是上传文件的文件名,如果是 IE6 则获取的是全路径,否则获取的是简单文件名。

            7、上传文件的细节问题

                (1)如果将服务器的上传目录暴露在外界能访问到的地方,那么用户可以写一个系统命令文件传至服务器,只有再访问执行。为了解决这个问题,有两种途径,一:将上传目录放在 WEB-INF 中。 二:放在服务器管理不到的目录内。

                (2)上传文件的乱码问题

                         一:普通表单项乱码,因为上传文件的 enctype 为 multipart/form-data 所以我们使用传统的 setCharacterEncoding 是不可行的。所以我们在获取到值的时候,必须先将其按照 ISO-8859-1 的形式转为字节,再按照 UTF-8 的形式转为字符。1 手工解决:String value = fileItem.getString("username"); value = new String(value.getBytes("iso-8859-1") ,"UTF-8"); 这是一种方法。2 组建解决:还可以使用 fileItem.getString("UTF-8") 来直接解决普通表单项的乱码。

                         二:上传文件的文件名乱码,通过解析器的方法更改字符编码。servletFileUpload.setHeaderEncoding("UTF-8");

                 (3)没有选择文件空提交问题

                          判断文件名是否为空 String filename = fileItem.getName(); 判断 filename 是否为空,如果为空则不处理。

                 (4)限制上传文件类型,判断文件是否为合法文件

                          通过截取文件扩展名,来匹配服务器中的集合,如果包含则允许执行上传,如果不包含则抛一个自定义编译时异常,在 catch 代码块中往 request 域中添加显示信息,转发到 Jsp 中给用户提示。

                 (5)限制上传文件大小

                           通过解析器来限定上传文件大小,servletFileUpload.setFileSizeMax(1024*1024); 其中限制的单位为 byte。如果超出限制大小,则抛出 FileSizeLimitExceedException 编译时异常。捕获异常的处理方式和上面一样。

                 (6)防止上传文件被覆盖的问题

                            服务器在保存每一个上传文件时,要为每一个文件生成一个唯一的文件名。我们采用 UUID + 真实文件名作为服务器存储的文件名。sdfe-123k-wdf3-sdfe4_1.jpg 。

                  (7)防止服务器一个存储目录文件数过多造成访问速度下降的问题

                             在 windows 操作系统下,如果一个目录中的文件数超过 1000 就会导致该目录的访问速度下降。为了解决这个问题,大致有两种算法 

                             一:将文件名进行 Base64 编码,首先取得编码后文件名的第一个字母,如果是 a 则存放在 a 目录中,如果是 b 则存放在 b 目录中,以此类推,可以分为 26 个目录存放,26 个目录每个目录存放 1000 个文件,那么可以存放 2.6 万个文件。如果都存满了,那么我们再取出文件名的第二个字母,如果一个文件前两个字母是 ac 那么我们将文件存放在 a 目录下面的 c 目录下,这样每个目录下又有了 26 个子目录,再以此类推 26*1000*26*1000*26*1000……这样存下去,我们可以将世界上的所有文件存放在服务器中,而且保证了服务器中的每个文件夹的文件个数不超过 1000。

                            二:将获取到的文件名进行 hash 运算,可以得到一个 32 位的整数,我们把 32 位整数与 0xF 进行按位与运算,可以得到第一个字节,这个字节可以表示 0-15 之间的任意数字,总共可以表示 16 个数,我们取出这个数作为服务器保存文件的第一个目录,之后我们再将之前的 32 位整数右移 4 位,可以得到第二个字节的 4 个二进制位,之后再与 0xF 位与运算,就得到了第二个 0-15 之间的数,我们用这个数作为二级目录,那么我们现在可以存放的文件总数就是 16*16*1000 如果不够的话,我们可以取出第三个字节,以此类推,我们可以得到足够多的目录来存放文件。

 

 

[java] view plain copy
  1. public String makeDir(String serverDir, String fileName){  
  2.     int hashCode = fileName.hashCode();  
  3.     int dir_1 = hashCode&0xf;  
  4.     int dir_2 = (hashCode>>4)&0xf;  
  5.     String path = serverDir + File.separator + dir_1 + File.separator + dir_2;  
  6.     File dir = new File(path);  
  7.     if(!dir.exists()){  
  8.         dir.mkdirs();  
  9.     }  
  10.     return path + File.separator + fileName;  
  11. }  


                    (8)注意临时文件删除的问题

                             解析器在解析 request 的上传文件时,如果文件大小超过缓冲区的大小,那么将采用临时文件缓存上传文件,如果在执行完上传以后,没有及时删除临时文件,则会导致服务器中保存了两份已上传的文件。所以我们在执行完上传,或者上传异常处理中,应该及时删除临时文件。fileItem.delete()

                     (9)通过监听器实现上传文件进度显示

                              在解析器解析 request 之前,可以通过解析器设置一个监听器 upload.setProgressListener(pListener);(匿名内部类)

[java] view plain copy
  1. package upload;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileOutputStream;  
  5. import java.io.IOException;  
  6. import java.io.InputStream;  
  7. import java.util.List;  
  8. import javax.servlet.ServletException;  
  9. import javax.servlet.http.HttpServlet;  
  10. import javax.servlet.http.HttpServletRequest;  
  11. import javax.servlet.http.HttpServletResponse;  
  12. import org.apache.commons.fileupload.FileItem;  
  13. import org.apache.commons.fileupload.FileUploadException;  
  14. import org.apache.commons.fileupload.ProgressListener;  
  15. import org.apache.commons.fileupload.disk.DiskFileItemFactory;  
  16. import org.apache.commons.fileupload.servlet.ServletFileUpload;  
  17.   
  18. @SuppressWarnings("serial")  
  19. public class UploadServlet extends HttpServlet {  
  20.   
  21.     @SuppressWarnings("unchecked")  
  22.     public void doGet(HttpServletRequest request, HttpServletResponse response)  
  23.             throws ServletException, IOException {  
  24.         DiskFileItemFactory factory = new DiskFileItemFactory();  
  25.         factory.setRepository(new File("d:/temp"));  
  26.         ServletFileUpload upload = new ServletFileUpload(factory);  
  27.         if (ServletFileUpload.isMultipartContent(request)) {  
  28.             try {  
  29.                 upload.setProgressListener(new ProgressListener() {  
  30.   
  31.                     public void update(long bytesRead, long contentLength,  
  32.                             int items) {  
  33.                         System.out.println("文件大小:" + contentLength + "b");  
  34.                         System.out.println("已上传:" + bytesRead + "b");  
  35.                     }  
  36.                 });  
  37.                 List<FileItem> list = upload.parseRequest(request);  
  38.                 for (FileItem item : list) {  
  39.                     if (item.isFormField()) {  
  40.                         System.out.println("普通字段:" + item.getString());  
  41.                     } else {  
  42.                         InputStream in = item.getInputStream();  
  43.                         String fileName = item.getName();  
  44.                         String path = makeDir(getServletContext().getRealPath(  
  45.                                 "/WEB-INF/upload"), fileName);  
  46.                         FileOutputStream fos = new FileOutputStream(path);  
  47.                         int len = 0;  
  48.                         byte[] b = new byte[1024];  
  49.                         while ((len = in.read(b)) != -1) {  
  50.                             fos.write(b, 0, len);  
  51.                         }  
  52.                         fos.close();  
  53.                     }  
  54.                 }  
  55.             } catch (FileUploadException e) {  
  56.                 request.setAttribute("message""上传失败。。");  
  57.                 request.getRequestDispatcher("/message").forward(request,  
  58.                         response);  
  59.             }  
  60.         }  
  61.     }  
  62.   
  63.     public String makeDir(String serverDir, String fileName) {  
  64.         int hashCode = fileName.hashCode();  
  65.         int dir_1 = hashCode & 0xf;  
  66.         int dir_2 = (hashCode >> 4) & 0xf;  
  67.         String path = serverDir + File.separator + dir_1 + File.separator  
  68.                 + dir_2;  
  69.         File dir = new File(path);  
  70.         if (!dir.exists()) {  
  71.             dir.mkdirs();  
  72.         }  
  73.         return path + File.separator + fileName;  
  74.     }  
  75.   
  76.     public void doPost(HttpServletRequest request, HttpServletResponse response)  
  77.             throws ServletException, IOException {  
  78.         doGet(request, response);  
  79.     }  
  80.   
  81. }  


                 8、实现动态添加删除上传输入项

[html] view plain copy
  1. <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>  
  2.   
  3. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
  4. <html>  
  5.   <head>  
  6.     <title></title>  
  7.     <script type="text/javascript">  
  8.         function addInput(){  
  9.             var div = document.createElement("div");  
  10.               
  11.             var fileInput = document.createElement("input");  
  12.             fileInput.type = "file";  
  13.             fileInput.name="addFile";  
  14.             div.appendChild(fileInput);  
  15.               
  16.             var delInput = document.createElement("input");  
  17.             delInput.type="button";  
  18.             delInput.value="删除";  
  19.             delInput.onclick=function del(){  
  20.                 this.parentNode.parentNode.removeChild(this.parentNode);  
  21.             };  
  22.             div.appendChild(delInput);  
  23.             document.getElementById("files").appendChild(div);  
  24.         }  
  25.     </script>  
  26.   </head>  
  27.     
  28.   <body>  
  29.     <form action="${pageContext.request.contextPath }/servlet/UploadServlet" method="post" enctype="multipart/form-data">  
  30.         上传用户:<input type="text" name="username"><br>  
  31.         <input type="file" name="f"><br>  
  32.         <div id="files">  
  33.         </div>  
  34.         <input type="button" value="添加上传文件" onclick="addInput()"><br>  
  35.         <input type="submit" value="上传"><br>  
  36.     </form>  
  37.   </body>  
  38. </html>  


二、文件下载

[java] view plain copy
  1. package download;  
  2.   
  3. import java.io.File;  
  4. import java.io.IOException;  
  5. import java.util.LinkedHashMap;  
  6. import java.util.Map;  
  7.   
  8. import javax.servlet.ServletException;  
  9. import javax.servlet.http.HttpServlet;  
  10. import javax.servlet.http.HttpServletRequest;  
  11. import javax.servlet.http.HttpServletResponse;  
  12.   
  13. public class ShowList extends HttpServlet {  
  14.   
  15.     public void doGet(HttpServletRequest request, HttpServletResponse response)  
  16.             throws ServletException, IOException {  
  17.         File file = new File(getServletContext().getRealPath("/WEB-INF/upload"));  
  18.         Map<String, String> map = new LinkedHashMap<String, String>();  
  19.         map = getResource(file, map);  
  20.         request.setAttribute("map", map);  
  21.         request.getRequestDispatcher("/showlist.jsp").forward(request, response);  
  22.     }  
  23.   
  24.     public void doPost(HttpServletRequest request, HttpServletResponse response)  
  25.             throws ServletException, IOException {  
  26.         doGet(request, response);  
  27.     }  
  28.   
  29.     public Map<String, String> getResource(File file,Map<String, String> map){  
  30.         if(!file.isDirectory()){  
  31.             String uuidname = file.getName();  
  32.             String realname = uuidname.substring(uuidname.indexOf("_")+1);  
  33.             map.put(uuidname, realname);  
  34.         }else{  
  35.             for (File child : file.listFiles()) {  
  36.                 getResource(child, map);  
  37.             }  
  38.         }  
  39.         return map;  
  40.     }  
  41. }  

[java] view plain copy
  1. package download;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileInputStream;  
  5. import java.io.IOException;  
  6. import java.net.URLEncoder;  
  7.   
  8. import javax.servlet.ServletException;  
  9. import javax.servlet.ServletOutputStream;  
  10. import javax.servlet.http.HttpServlet;  
  11. import javax.servlet.http.HttpServletRequest;  
  12. import javax.servlet.http.HttpServletResponse;  
  13.   
  14. public class DownLoadServlet extends HttpServlet {  
  15.   
  16.     public void doGet(HttpServletRequest request, HttpServletResponse response)  
  17.             throws ServletException, IOException {  
  18.         String path = new String(request.getParameter("id").getBytes("iso-8859-1"), "utf-8");  
  19.         String realname = path.substring(path.lastIndexOf("\\") + 1);  
  20.         File file = new File(getFilepath(getServletContext().getRealPath(  
  21.                 "/WEB-INF/upload"), realname));  
  22.         realname = file.getName().substring(file.getName().indexOf("_") + 1);  
  23.         response.setHeader("content-disposition""attachment;filename="+ URLEncoder.encode(realname,"utf-8"));  
  24.         ServletOutputStream outputStream = response.getOutputStream();  
  25.         FileInputStream fis = new FileInputStream(file);  
  26.         int len = 0;  
  27.         byte[] b = new byte[1024];  
  28.         while ((len = fis.read(b)) != -1) {  
  29.             outputStream.write(b, 0, len);  
  30.         }  
  31.         fis.close();  
  32.     }  
  33.   
  34.     public void doPost(HttpServletRequest request, HttpServletResponse response)  
  35.             throws ServletException, IOException {  
  36.         doGet(request, response);  
  37.     }  
  38.   
  39.     public String getFilepath(String serverPath, String filepath) {  
  40.         int hashCode = filepath.hashCode();  
  41.         int dir_1 = hashCode & 0xf;  
  42.         int dir_2 = (hashCode >> 4) & 0xf;  
  43.         String path = serverPath + File.separator + dir_1 + File.separator  
  44.                 + dir_2 + File.separator + filepath;  
  45.         return path;  
  46.     }  
  47. }  
[html] view plain copy
  1. <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>  
  2. <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>  
  3.   
  4. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
  5. <html>  
  6.   <head>  
  7.     <title>My JSP 'index.jsp' starting page</title>  
  8.   </head>  
  9.     
  10.   <body>  
  11.     <c:forEach var="file" items="${requestScope.map}">  
  12.         <c:url var="path" value="/servlet/DownLoadServlet">  
  13.             <c:param name="id" value="${file.key }" />  
  14.         </c:url>  
  15.         ${file.value } <a href="${path }">下载</a><br>  
  16.     </c:forEach>  
  17.   </body>  
  18. </html>  

转载来自:http://blog.csdn.net/driverking/article/details/6750802