J2EE进阶之上传和下载 二十二

上传和下载

一、文件上传必须要前提(记住)

- 1、表单的method属性必须是post
- 2、表单要提供<input type=”file” name=”photo”/>的上传输入域。 
- 3、表单的enctype属性必须是multipart/form-data类型。
        enctype:
        作用:HTTP协议中要出现请求正文,method必须是POST方式。
        他的作用就是相当于Content-Type的请求消息头的作用:告知服务器,请求正文的MIME类型。

二、文件上传的原理分析(理解)

enctype属性的常用取值

application/x-www-form-urlencoded(默认值)

username=abc&password=123&gender=male           
String value = request.getParameter(String name)(服务器获取值的方法)

multipart/form-data

两种属性的情况,第二种我们就需要取值操作。。

三、借助第三方组件实现文件上传(会做)

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

使用Commons-fileupload组件实现文件上传,需要导入该组件相应的支撑jar包:Commons-fileuploadcommons-io。commons-io 不属于文件上传组件的开发jar文件,但Commons-fileupload 组件从1.1 版本开始,它工作时需要commons-io包的支持。

demo测试

  1 import java.io.File;
  2 import java.io.FileOutputStream;
  3 import java.io.IOException;
  4 import java.io.InputStream;
  5 import java.io.OutputStream;
  6 import java.util.ArrayList;
  7 import java.util.List;
  8 
  9 import javax.servlet.ServletException;
 10 import javax.servlet.http.HttpServlet;
 11 import javax.servlet.http.HttpServletRequest;
 12 import javax.servlet.http.HttpServletResponse;
 13 
 14 import org.apache.commons.fileupload.FileItem;
 15 import org.apache.commons.fileupload.FileUploadException;
 16 import org.apache.commons.fileupload.disk.DiskFileItemFactory;
 17 import org.apache.commons.fileupload.servlet.ServletFileUpload;
 18 //借助commons-fileupload实现文件上传:入门案例
 19 public class UploadServlet2 extends HttpServlet {
 20 
 21     public void doGet(HttpServletRequest request, HttpServletResponse response)
 22             throws ServletException, IOException {
 23         response.setContentType("text/html;charset=UTF-8");
 24         
 25         //判断表单提交的内容是不是multipart/form-data类型的
 26         boolean isMultipart = ServletFileUpload.isMultipartContent(request);
 27         if(!isMultipart){
 28             throw new RuntimeException("你这个傻叉,表单的enctype必须是multipart/form-data");
 29         }
 30         //解析请求中的正文内容:FileItem---代表者表单的每一项输入域
 31         DiskFileItemFactory factory = new DiskFileItemFactory();
 32         ServletFileUpload sfu = new ServletFileUpload(factory);
 33         List<FileItem> items = new ArrayList<FileItem>();
 34         try {
 35             items = sfu.parseRequest(request);
 36         } catch (FileUploadException e) {
 37             throw new RuntimeException("解析上传内容失败");
 38         }
 39         //遍历:
 40         for(FileItem item:items){
 41             //是普通字段:把字段名和值打印到控制台上
 42             if(item.isFormField()){
 43                 processFormField(item);
 44             }else{
 45             //是上传字段:把上传的文件保存在应用的\files目录中
 46                 try {
 47                     processUploadField(item);
 48                 } catch (Exception e) {
 49                     e.printStackTrace();
 50                 }
 51             }
 52         }
 53         
 54         response.getOutputStream().write("上传成功".getBytes("UTF-8"));
 55     }
 56     //是上传字段:把上传的文件保存在应用的\files目录中
 57     private void processUploadField(FileItem item) throws Exception {
 58         //得到保存文件的真实目录
 59         String storeDirectory = getServletContext().getRealPath("/files");
 60         //不在的话:创建他
 61         File root = new File(storeDirectory);
 62         if(!root.exists()){
 63             root.mkdirs();
 64         }
 65         //得到文件名
 66         String filename = item.getName();
 67         //输入流和输出流:搞
 68         InputStream  in = item.getInputStream();
 69         OutputStream out = new FileOutputStream(new File(storeDirectory, filename));
 70         int len = -1;
 71         byte b[] = new byte[1024];
 72         while((len=in.read(b))!=-1){
 73             out.write(b, 0, len);
 74         }
 75         in.close();
 76         out.close();
 77     }
 78     
 79     //是普通字段:把字段名和值打印到控制台上
 80     private void processFormField(FileItem item) {
 81         String fieldName = item.getFieldName();
 82         String fieldValue = item.getString();
 83         System.out.println(fieldName+"="+fieldValue);
 84     }
 85 
 86     public void doPost(HttpServletRequest request, HttpServletResponse response)
 87             throws ServletException, IOException {
 88         doGet(request, response);
 89     }
 90 
 91 }

四、文件上传编程时要考虑的几个问题(思考)

1:保护上传文件和服务器的安全

解决办法:把保存上传文件的目录存放在让客户端直接访问不到的地方

2、避免同一文件夹下的文件重名

把文件名改为唯一的即可。

3、避免同一文件夹下的文件过多

分目录进行存储

方案一:按照日期生成存储目录

方案二:按照文件名的hash码计算子目录:a\b

4、中文乱码问题

a、普通字段的中文乱码

FileItem.getString(String charset);

b、上传的中文文件名乱码问题

request.setCharacterEncoding("UTF-8");//只能解决上传的文件名是中文的乱码

5、限制上传文件的大小

Web方式下不适合上传特别大的文件

a、限制单个文件大小

b、限制总文件大小:一次多文件上传时

6、限制上传文件的类型

控制上传文件的扩展名和判断上传文件的MIME类型。

IE浏览器:上传时把文件扩展名改掉后,他的MIME类型没有变

a.txt   MIME:text/plain
a.jpg   (还是那个文本)    MIME:text/plain

火狐:a.txt MIME:text/plain

a.jpg(还是那个文本) MIME:image/jpg

更加专业:取得文件的二进制,判断文件类型。

7、多文件上传时,没有选择上传文件的问题

用JS进行判断

服务器端验证:不要判断FileItem是不是空,因为不管用户上不上传,该对象都不是空。要通过文件的文件名来判断。

8、临时文件的问题

默认情况下,上传的文件超过10Kb,commons-fileupload就会采用临时文件。

在上传时,流关闭后,调用FileItem.delete()删除临时文件。

完整demo

  1 //上传时要考虑的问题
  2 public class UploadServlet3 extends HttpServlet {
  3 
  4     public void doGet(HttpServletRequest request, HttpServletResponse response)
  5             throws ServletException, IOException {
  6         request.setCharacterEncoding("UTF-8");//只能解决上传的文件名是中文的乱码
  7         response.setContentType("text/html;charset=UTF-8");
  8         
  9         //判断表单提交的内容是不是multipart/form-data类型的
 10         boolean isMultipart = ServletFileUpload.isMultipartContent(request);
 11         if(!isMultipart){
 12             throw new RuntimeException("你这个傻叉,表单的enctype必须是multipart/form-data");
 13         }
 14         //解析请求中的正文内容:FileItem---代表者表单的每一项输入域
 15         DiskFileItemFactory factory = new DiskFileItemFactory();
 16         
 17 //      factory.setSizeThreshold(10*1024);//设置缓存的大小。默认10kb。
 18 //      factory.setRepository(new File("d:/"));//设置临时文件存放的目录。默认值是系统的临时文件目录
 19         
 20         ServletFileUpload sfu = new ServletFileUpload(factory);
 21 //      sfu.setFileSizeMax(4*1024*1024);//限制单个文件上传大小不能超过4M
 22 //      sfu.setSizeMax(6*1024*1024);//限制总文件大小不能超过6M
 23         List<FileItem> items = new ArrayList<FileItem>();
 24         try {
 25             items = sfu.parseRequest(request);
 26         } catch(FileUploadBase.FileSizeLimitExceededException e){
 27             response.getOutputStream().write("单个文件大小不能超出4M".getBytes("UTF-8"));
 28         }catch(FileUploadBase.SizeLimitExceededException e){
 29             response.getOutputStream().write("总文件大小不能超出6M".getBytes("UTF-8"));
 30         }catch (FileUploadException e) {
 31             e.printStackTrace();
 32             throw new RuntimeException("解析上传内容失败");
 33         }
 34         //遍历:
 35         for(FileItem item:items){
 36             //是普通字段:把字段名和值打印到控制台上
 37             if(item.isFormField()){
 38                 processFormField(item);
 39             }else{
 40             //是上传字段:把上传的文件保存在应用的\files目录中
 41                 try {
 42                     processUploadField(item);
 43                 } catch (Exception e) {
 44                     e.printStackTrace();
 45                 }
 46             }
 47         }
 48         
 49         response.getOutputStream().write("上传成功".getBytes("UTF-8"));
 50     }
 51     //是上传字段:把上传的文件保存在应用的\files目录中
 52     private void processUploadField(FileItem item) throws Exception {
 53         //得到保存文件的真实目录
 54         String storeDirectory = getServletContext().getRealPath("/WEB-INF/files");
 55         //不在的话:创建他
 56         File root = new File(storeDirectory);
 57         if(!root.exists()){
 58             root.mkdirs();
 59         }
 60         //得到文件名
 61         String filename = item.getName();//  C:\Users\wzhting\Desktop\a.txt       a.txt 与浏览器有关
 62         if(filename!=null){
 63             
 64             //判断文件的扩展名只能上传文本文件
 65 //          String extensionName = FilenameUtils.getExtension(filename);
 66 //          if(!"txt".equals(extensionName)){
 67 //              return;
 68 //          }
 69             
 70             filename = FilenameUtils.getName(filename);//   a.txt
 71 //          System.out.println(filename+"MIME类型:"+item.getContentType());
 72         }
 73         //把文件名改为唯一的
 74         filename = UUID.randomUUID()+"_"+filename;
 75         
 76         //计算一个子目录:存储文件的
 77         String childDirectory = makeChildDirectory(storeDirectory,filename);
 78         
 79         
 80         //输入流和输出流:搞
 81         /*
 82         InputStream  in = item.getInputStream();
 83         OutputStream out = new FileOutputStream(new File(storeDirectory+File.separator+childDirectory, filename));
 84         int len = -1;
 85         byte b[] = new byte[1024];
 86         while((len=in.read(b))!=-1){
 87             out.write(b, 0, len);
 88         }
 89         in.close();
 90         out.close();
 91         item.delete();// 删除临时文件
 92         */
 93         item.write(new File(storeDirectory+File.separator+childDirectory, filename));
 94     }
 95     ////按照文件名的哈希吗生成子目录:返回的是子目录名称  1\3
 96     private String makeChildDirectory(String storeDirectory, String filename) {
 97         int hashCode = filename.hashCode();
 98         int dir1 = hashCode&0xf;//取0~3位
 99         int dir2 = (hashCode&0xf0)>>4;//取4~7位
100         /*
101         1101 1111 0101 0111 1101 1111 0101 0111 hashCode
102         0000 0000 0000 0000 0000 0000 0000 1111  &
103         ---------------------------------------------
104         0000 0000 0000 0000 0000 0000 0000 0111    范围:0000~1111转成10进制:0~15 
105         
106         1101 1111 0101 0111 1101 1111 0101 0111 hashCode
107         0000 0000 0000 0000 0000 0000 1111 0000 &0xf0 
108         ---------------------------------------------
109         0000 0000 0000 0000 0000 0000 0101 0000  >>4
110         ---------------------------------------------
111         0000 0000 0000 0000 0000 0000 0000 0101  范围:0000~1111转成10进制:0~15
112          */
113         String childDirectory = dir1+File.separator+dir2;//       1\3
114         File directory = new File(storeDirectory, childDirectory);
115         if(!directory.exists()){
116             directory.mkdirs();
117         }
118         return childDirectory;
119     }
120     //按照日期生成子目录:返回的是子目录名称  2014-11-28
121     private String makeChildDirectory(String storeDirectory) {
122         Date now = new Date();
123         String childDirectory = new SimpleDateFormat("yyyy-MM-dd").format(now);
124         //判断子目录在不在:不在创建他
125         File directory = new File(storeDirectory, childDirectory);
126         if(!directory.exists()){
127             directory.mkdirs();
128         }
129         return childDirectory;
130     }
131     //是普通字段:把字段名和值打印到控制台上
132     private void processFormField(FileItem item) {
133         try {
134             String fieldName = item.getFieldName();
135             String fieldValue = item.getString("UTF-8");
136             System.out.println(fieldName+"="+fieldValue);
137         } catch (UnsupportedEncodingException e) {
138             e.printStackTrace();
139         }
140     }
141 
142     public void doPost(HttpServletRequest request, HttpServletResponse response)
143             throws ServletException, IOException {
144         doGet(request, response);
145     }
146 
147 }

五、文件的下载(会做)

查询可下载的所有文件

  1 import java.io.File;
  2 import java.io.IOException;
  3 import java.util.HashMap;
  4 import java.util.Map;
  5 
  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 ShowAllFilesServlet extends HttpServlet {
 12 
 13     public void doGet(HttpServletRequest request, HttpServletResponse response)
 14             throws ServletException, IOException {
 15         //key:uuidFilename value:oldFilename
 16         //key:1c2d33b7-b4e4-4cc0-80e4-f2d8a1c94e94_1_1.jpg
 17         //value:1_1.jpg
 18         
 19         Map<String, String> map = new HashMap<String, String>();
 20         //得到WEB-INF/files的真实目录,对里面的文件进行递归
 21         String storeDirectory = getServletContext().getRealPath("/WEB-INF/files");
 22         File root = new File(storeDirectory);
 23         treeWalk(root,map);
 24         //Map中就存了文件的文件名
 25         request.setAttribute("map", map);
 26         request.getRequestDispatcher("/listFiles.jsp").forward(request, response);
 27     }
 28 
 29     private void treeWalk(File root, Map<String, String> map) {
 30         if(root.isFile()){
 31             String uuidFilename = root.getName();
 32             String oldFilename = uuidFilename.substring(uuidFilename.indexOf("_")+1);
 33             map.put(uuidFilename, oldFilename);
 34         }else{
 35             File[] files = root.listFiles();
 36             for(File file:files){
 37                 treeWalk(file, map);
 38             }
 39         }
 40     }
 41 
 42     public void doPost(HttpServletRequest request, HttpServletResponse response)
 43             throws ServletException, IOException {
 44         doGet(request, response);
 45     }
 46 
 47 }

js页面

  <body>
    <h1>本站有以下好资源</h1>
    <c:forEach items="${map}" var="me">
        <c:url value="/servlet/DownloadServlet" var="url">
            <c:param name="filename" value="${me.key}"></c:param>
        </c:url>
        ${me.value}&nbsp;&nbsp;<a href="${url}">下载</a><br/>
    </c:forEach>
  </body>

下载

  1 import java.io.File;
  2 import java.io.FileInputStream;
  3 import java.io.IOException;
  4 import java.io.InputStream;
  5 import java.io.OutputStream;
  6 import java.net.URLEncoder;
  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 DownloadServlet extends HttpServlet {
 14 
 15     public void doGet(HttpServletRequest request, HttpServletResponse response)
 16             throws ServletException, IOException {
 17         response.setContentType("text/html;charset=UTF-8");
 18         //得到要下载的文件名
 19         String uuidFilename = request.getParameter("filename");//5eaad6f3-ce5e-44c2-8885-538650e890de_%e7%be%8e%e5%a5%b3.jpg
 20         //get方式提交过来的请求参数
 21         uuidFilename = new String(uuidFilename.getBytes("ISO-8859-1"),"UTF-8");//5eaad6f3-ce5e-44c2-8885-538650e890de_美女.jpg
 22         //他在哪个目录中
 23         String storeDirectory = getServletContext().getRealPath("/WEB-INF/files");
 24         String childDirectory = makeChildDirectory(storeDirectory,uuidFilename);
 25         
 26         //判断该文件在不在
 27         File file = new File(storeDirectory+File.separator+childDirectory,uuidFilename);
 28         if(!file.exists()){
 29             response.getOutputStream().write("文件已经不存在了".getBytes("UTF-8"));
 30             return;
 31         }
 32         //文件存在
 33         //截取原来文件名
 34         String oldFileName = uuidFilename.substring(uuidFilename.indexOf("_")+1);//有可能是中文
 35         response.setHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode(oldFileName, "UTF-8"));
 36         
 37         InputStream in = new FileInputStream(file);
 38         OutputStream out = response.getOutputStream();
 39         int len = -1;
 40         byte b[] = new byte[1024];
 41         while((len=in.read(b))!=-1){
 42             out.write(b, 0, len);
 43         }
 44         in.close();
 45 //      response.getOutputStream().write("下载成功".getBytes("UTF-8"));
 46     }
 47 
 48     public void doPost(HttpServletRequest request, HttpServletResponse response)
 49             throws ServletException, IOException {
 50         doGet(request, response);
 51     }
 52     private String makeChildDirectory(String storeDirectory, String filename) {
 53         int hashCode = filename.hashCode();
 54         int dir1 = hashCode&0xf;//取0~3位
 55         int dir2 = (hashCode&0xf0)>>4;//取4~7位
 56         String childDirectory = dir1+File.separator+dir2;//       1\3
 57         File directory = new File(storeDirectory, childDirectory);
 58         if(!directory.exists()){
 59             directory.mkdirs();
 60         }
 61         return childDirectory;
 62     }
 63 }

猜你喜欢

转载自blog.csdn.net/onceing/article/details/77842070
今日推荐