文件上传



一、文件上传的原理
        1、文件上传的前提:
                a、form表单的method必须是post
                b、form表单的enctype必须是multipart/form-data(决定了POST请求方式,请求正文的数据类型)
                          注意:当表单的enctype是multipart/form-data,传统的获取请求参数的方法失效。
                          
                          ---------------------------------------------------------------------------------------------------------------------------
                          请求正文:(MIME协议进行描述的,正文是多部分组成的)
                          -----------------------------7dd32c39803b2    (// 分界符)
                          Content-Disposition: form-data; name="username"


                          wzhting
                          -----------------------------7dd32c39803b2
                          Content-Disposition: form-data; name="f1"; filename="C:\Documents and Settings\wzhting\妗岄潰\a.txt"
                          Content-Type: text/plain


                          aaaaaaaaaaaaaaaaaa
                          -----------------------------7dd32c39803b2
                          Content-Disposition: form-data; name="f2"; filename="C:\Documents and Settings\wzhting\妗岄潰\b.txt"
                          Content-Type: text/plain


                          bbbbbbbbbbbbbbbbbbb
                          -----------------------------7dd32c39803b2--
                          ---------------------------------------------------------------------------------------------------------------------------
                          
                c、form中提供input的type是file类型的文件上传域



二、利用第三方组件实现文件上传


            1、commons-fileupload组件:
                        jar:commons-fileupload.jar 和 commons-io.jar
                        
            2、 fileupload组件工作流程
                       
            
            3、核心类或接口
                    DiskFileItemFactory:设置环境
                                  public void setSizeThreshold(int?sizeThreshold) :
                                                      设置缓冲区大小。默认是10Kb。
                                                      当上传的文件超出了缓冲区大小,fileupload组件将使用临时文件缓存上传文件
                                  public void setRepository(java.io.File repository):
                                                     设置临时文件的存放目录。默认是系统的临时文件存放目录。
                          
                    ServletFileUpload:核心上传类(主要作用:解析请求的正文内容)
                                  boolean isMultipartContent(HttpServletRequest?request):
                                                      判断用户的表单的enctype是否是multipart/form-data类型的。
                                  List parseRequest(HttpServletRequest request):
                                                      解析请求正文中的内容
                                  setFileSizeMax(4*1024*1024);           //设置单个上传文件的大小
                                  upload.setSizeMax(6*1024*1024);   //设置总文件大小
                            
                    FileItem:代表表单中的一个输入域。
                                  boolean isFormField():     是否是普通字段
                                  String getFieldName:    获取普通字段的字段名
                                  String getString():            获取普通字段的值
                                  
                                  InputStream getInputStream():      获取上传字段的输入流
                                  String getName():                            获取上传的文件名                                 
                                  getContentType();                            得到上传文件的MIME类型
                                  
                                  FileItem.delete();                              方法删除临时文件。但一定要在关闭流之后。
                                  
          4、 文件上传案例          
                        -------------------------------------------------------------------------------------------------------------
                        <body>
                              <form action="${pageContext.request.contextPath}/servlet/UploadServlet3" enctype="multipart/form-data" method="post">         
                                      用户名: <input type="text" name="username" /> <br/><br/>
                                      文件1: <input type="file" name="f1" /> <br/><br/>
                                      文件2: <input type="file" name="f2" /> <br/><br/>
                                      <input type="submit" value="保存" />
                              </form>    
                        </body>
                        -------------------------------------------------------------------------------------------------------------
                        // 存储目的地: tomacate/webapp/day21/files 
                        public class UploadServlet2 extends HttpServlet {
                        
                                public void doGet(HttpServletRequest request, HttpServletResponse response)
                                    throws ServletException, IOException {
                                        try {
                                                // 设置环境
                                                DiskFileItemFactory factory = new DiskFileItemFactory();
                                                // 得到存放上传文件的真实路径
                                                String storePath = getServletContext().getRealPath("/WEB-INF/files");
                                                
                                                // 判断一下form是否为enctype=multipart/file-data
                                                boolean isMultipart = ServletFileUpload.isMultipartContent(request);
                                                if(!isMultipart){
                                                        System.out.println("大傻鸟,快去将表单的enctype的值设置为multipart/form-data");
                                                        return;
                                                }
                                          
                                                // ServletFileUpload核心类
                                                ServletFileUpload upload = new ServletFileUpload(factory);
                                                // 解析
                                                List<FileItem> items = upload.parseRequest(request);
                                                for(FileItem item : items){
                                                        if(item.isFormField()){
                                                                // 是普通字段
                                                                String fieldName = item.getFieldName();
                                                                String fieldValue = item.getString();
                                                                System.out.println(fieldName+"="+fieldValue);
                                                        }else{
                                                                // 是上传字段
                                                                InputStream in = item.getInputStream();
                                                                // 上传的文件名
                                                                // 注: 有的浏览器为: c:\Documents and Settings\wzhting\妗岄潰\a.txt    有的为  a.txt         
                                                                String fileName = item.getName();   
                                                                fileName = fileName.substring(fileName.lastIndexOf("\\")+1);   // 得到文件名:  a.txt
                                                                // 构件输出流
                                                                String storeFile = storePath + "\\" + fileName;
                                                                OutputStream out = new FileOutputStream(storeFile);
                                                                byte[] b = new byte[1024];
                                                                int len = 0;
                                                                while((len=in.read(b))!=-1){
                                                                        out.write(b, 0, len);
                                                                }
                                                                out.close();
                                                                in.close();
                                                        }
                                                }
                                        } catch (FileUploadException e) {
                                                throw new RuntimeException("服务器繁忙");
                                        }
                                }


                                public void doPost(HttpServletRequest request, HttpServletResponse response)
                                    throws ServletException, IOException {
                                        doGet(request, response);
                                }
                                
                        }
                        -------------------------------------------------------------------------------------------------------------
          

三、文件上传中要注意的9个问题
            1、如何保证服务器的安全
                        把保存上传文件的目录放到WEB-INF目录中。
                        
            2、中文乱码问题
                        2.1普通字段的中文请求参数
                                解决办法:String value = FileItem.getString("UTF-8");
                        2.2上传的文件名是中文
                                解决办法:request.setCharacterEncoding("UTF-8");
                                
            3、重名文件被覆盖的问题
                       
                        System.currentMillions()+"_"+a.txt(乐观, 可能重复)
                        UUID+"_"+a.txt:保证文件名唯一
                      
            4、分目录存储上传的文件
                        方式一:当前日期建立一个文件夹,当前上传的文件都放到此文件夹中。
                        方式二:利用文件名的hash码打散目录来存储。
                                      --------------------------------------------------------------------------------------
                                            int hashCode = fileName.hashCode();
                                            执行运算;   hashCode&0xf; 
                                                              1001 1010 1101 0010 1101 1100 1101 1010
                                                          & 0000 0000 0000 0000 0000 0000 0000 1111 
                                                              ---------------------------------------------------
                                                              0000 0000 0000 0000 0000 0000 0000 1010   
                                                              取hashCode的后4位   范围:0000~1111:整数0~15共16个
                                              
                                             执行运算: (hashCode&0xf0)>>4
                                                              1001 1010 1101 0010 1101 1100 1101 1010
                                                        &   0000 0000 0000 0000 0000 0000 1111 0000  
                                                              ---------------------------------------------------
                                                              0000 0000 0000 0000 0000 0000 1101 0000  >>4
                                                              ---------------------------------------------------
                                                              0000 0000 0000 0000 0000 0000 0000 1101
                                                                                0000~1111:整数0~15共16个
                                   --------------------------------------------------------------------------------------
                                    
            5、限制用户上传的文件类型
                        通过判断文件的扩展名来限制是不可取的。
                        通过判断其Mime类型才靠谱。FileItem.getContentType();
              
            6、如何限制用户上传文件的大小
                        6.1单个文件大小限制。超出了大小友好提示
                                    抓异常进行提示:
                                          org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException
                        6.2总文件大小限制。超出了大小友好提示
                                    抓异常进行提示:
                                          org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException
                
            7、临时文件的问题
                        commons-fileupload组件不会删除超出缓存的临时文件。                       
                        FileItem.delete()方法删除临时文件。但一定要在关闭流之后。
              
            8、多个文件上传时,没有上传内容的问题
                        if(fileName==null||"".equals(fileName.trim())){
                                continue;
                        }
                      
            9、上传进度检测
                      给ServletFileUpload注册一个进度监听器即可,把上传进度传递给页面去显示
                            //pBytesRead:当前以读取到的字节数
                            //pContentLength:文件的长度
                            //pItems:第几项
                      public void update(long pBytesRead, long pContentLength,
                          int pItems) {
                              System.out.println("已读取:"+pBytesRead+",文件大小:"+
                                                                    pContentLength+",第几项:"+pItems);
                      }
                      
                      
             例:
             --------------------------------------------------------------------------------------------------
               public class UploadServlet3 extends HttpServlet {


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


                              // 解决上传文件名的中文编码问题
                              request.setCharacterEncoding("UTF-8");
                              response.setContentType("text/html;charset=UTF-8");
                              PrintWriter pw = response.getWriter();
                              
                              try {
                                      // 设置环境
                                      DiskFileItemFactory factory = new DiskFileItemFactory();
                                      // 设置临时存放目录  在webroot下创建一个temp的文件夹  查看的时候在apatch查看
                                      factory.setRepository(new File(getServletContext().getRealPath("/temp")));
                                      // 得到存放上传文件的真实路径
                                      String storePath = getServletContext().getRealPath("/WEB-INF/files");
                                      
                                      // 判断一下form是否为enctype=multipart/file-data
                                      boolean isMultipart = ServletFileUpload.isMultipartContent(request);
                                      if(!isMultipart){
                                              System.out.println("大傻鸟,快去将表单的enctype的值设置为multipart/form-data");
                                              return;
                                      }
                                      
                                      // ServletFileUpload核心类
                                      ServletFileUpload upload = new ServletFileUpload(factory);
                                      // 进度监听
                                      upload.setProgressListener(new ProgressListener() {
                                              // 关联源代码(common-fileupload.jar 显示参数)
                                              // pBytesRead: 当前已读取到的字节数    pContentLength: 文件的长度   pItem: 第几项,从1开始
                                              public void update(long pBytesRead, long pContentLength,int pItems) {
                                                      System.out.println("已读取:"+pBytesRead+",文件大小:"+pContentLength+
                                                                                      ",第几项:"+pItems);
                                              }
                                      });
                                      
                                      // 设置单个上传文件的大小不能超过8M
                                // upload.setFileSizeMax(8*1024*1024);
                                      // 设置上传的总文件的大小不能超过15M
                                // upload.setSizeMax(15*1024*1024);
                                      
                                      // 解析
                                      List<FileItem> items = upload.parseRequest(request);
                                      for(FileItem item : items){
                                              if(item.isFormField()){
                                                      // 是普通字段
                                                      String fieldName = item.getFieldName();
                                                      String fieldValue = item.getString("UTF-8");  // 解决form表单请求参数的中文编码问题
                                                      System.out.println(fieldName+"="+fieldValue);
                                              }else{
                                                
                                                      // 得到上传文件的MIME类型
                                            // String mimeType = item.getContentType();
                                                      // 限制上传文件的类型: 只允许为图片类型
                                            // if(mimeType.startsWith("image")){
                                                        
                                                              // 是上传字段
                                                              InputStream in = item.getInputStream();
                                                              // 上传的文件名
                                                              String fileName = item.getName();   // 注: 有的浏览器为: c:\Documents and Settings\wzhting\妗岄潰\a.txt    有的为  a.txt
                                                              // 如果上传的文件为null
                                                              if(fileName==null||"".equals(fileName.trim())){
                                                                      continue;
                                                              }
                                                              
                                                              fileName = fileName.substring(fileName.lastIndexOf("\\")+1);   // 得到文件名:  a.txt
                                                              fileName = UUID.randomUUID()+"_"+fileName;  // 保证新的文件名不重复,避免了上传的文件的重名覆盖问题
                                                              
                                                              // 打散存储目录
                                                              String newStorePath = makeStorePath(storePath,fileName);   // 根据WEB-INF/files和文件名,创建一个新的存储路径: /WEB-INF/files/1/13/文件名            
                                                              String storeFile = newStorePath + "\\" + fileName;   // WEN-INF/files/1/13/文件名
                                                              // 构件输出流
                                                              OutputStream out = new FileOutputStream(storeFile);
                                                              byte[] b = new byte[1024];
                                                              int len = 0;
                                                              while((len=in.read(b))!=-1){
                                                                      out.write(b, 0, len);
                                                              }
                                                              out.close();
                                                              in.close();
                                                              // 删除临时文件   注:一定要在关闭流之后  临时文件在上传完毕后会调用该方法自动删除
                                                              item.delete();
                                                        }
                                          // }
                                        }
                              } catch (org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException e){
                                      // 该异常为: 单个文件超出大小时的异常     注: 异常的捕获: 子类异常必须在父类异常的前面捕获  
                                      pw.write("单个文件的大小不能超过8M");
                              } catch (org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException e){
                                      // 该异常为: 上传的总文件超出大小时的异常
                                      pw.write("上传的总文件的大小不能超过15M");
                              }catch (FileUploadException e) {
                                      e.printStackTrace();
                              } 
                      }


                            
                    // 根据/WEN-INF/files和文件名 创建一个新的存储路径: /WEB-INF/files/1/13
                    private String makeStorePath(String storePath, String fileName) {
                            int hashCode = fileName.hashCode();
                            int dir1 = hashCode&0xf;        // 0000~1111  整数0~15 共16个
                            int dir2 = (hashCode&0xf0)>>4; // 0000~1111  整数0~15 共16个
                            String path = storePath+"\\"+dir1+"\\"+dir2;  // WEB-INF/files/1/13
                            File file = new File(path);
                            if(!file.exists()){
                                    file.mkdirs();
                            }
                            return path;
                      }


                      public void doPost(HttpServletRequest request, HttpServletResponse response)
                          throws ServletException, IOException {
                              doGet(request, response);
                      }


              }
       
             --------------------------------------------------------------------------------------------------         
             
             
             
             
             
             
                      
                      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值