• 文件上传


    一、概述

    FileUpload能够以多种不同的方式使用,具体取决于应用程序的要求。在最简单的情况下,调用单个方法来解析servlet请求,然后处理解析出来的Item集合。此外也可以自定义FileUpload已完全控制各个Item的存储方式,比如设置缓存目录、直接将接收到的Item以流的形式写入数据库等。

    FileUpload依赖于Commons IO,因此类路径下要有Commons IO的jar包。当然采用Maven依赖的方式不用担心,maven会自动下载Commons IO包。

    二、FIleUpload使用

    上传文件,注意几个问题

    ① form表单内,要添加空间<input type="file" name="myfile">
    ② form表单的内容格式要定义成multipart/form-data格式:<form action="admin/good_add" method="post" enctype="multipart/form-data">
    ③ 需要类库:1 commons-io.jar ,2 commons-fileupload-1.3.1.jar

    接下来我们看下他的用法。

    1)创建工厂类

    DiskFileItemFactory factory = new DiskFileItemFactory();

    FileItemFactory可以设置内容:

     ● setRepository(File dir);//设置临时文件存储位置。

     ● setSizeThreshold(long bytes);//设置请求大小阈值,当请求大于该值时,接收到的数据是缓存在磁盘中的,否则直接缓存到内存中。

     ● setFileCleaningTracker(FileCleaningTracker pTracker);//设置临时文件清理跟踪器

    2)创建核心类-文件解析对象

    ServletFileUpload upload = new ServletFileUpload(DiskFileItemFactory factory);

    ServletFileUpload可以设置内容:

     ● setSizeMax(long bytes);//设置整个请求的最大值,大于该值时,不允许传送。

     ● setFileMaxSize(long bytes);//设置单个文件的最大值,大于该值时,不允许传送。

     ● setHeaderEncoding(String charset);//设置读取每个FileItem的头数据的字符编码,不设置时采用request编码,也没有时采用系统默认编码。

     ● serProgressListener(ProgressListener pListener);//设置上传进度监听器

    3)解析request请求-进行文件解析后放在List中,因为这个类库支持多个文件上传,因此把结果会存在List中。

    List<FileItem> list = upload.parseRequest(request);

    FileItem能够获取的内容:

     ● getContentType();//获取单个Item的ContentType

     ● getName();//获取Item本来的文件名,如果不是文件则为null

     ● getFieldName();//获取Item的field名

     ● getSize();//获取Item的大小

     ● get();//将Item转换成字节数组返回

     ● isInMemory();//Item目前是否存在内存中

     ● isFormField();//是否是表单域

     ● getInputStream();//获取输入流,用于读取Item

    二、上传原理和代码分析

    上传:将需要上传的资源,发送给服务器,在服务器中保存下来。

    下载:下载资源时,将服务器上的某个资源发送给浏览器。

     

    1)浏览器端

    注意:enctype=multipart/form-data:该属性表明发送的请求体的内容是多表单元素的,通俗点讲,就是有各种各样的数据,可能有二进制数据,也可能有表单数据,等等。

     1 <form class="form-horizontal" action="${pageContext.request.contextPath }/admin/good_add" method="post" enctype="multipart/form-data">
     2     <div class="form-group">
     3         <label for="input_name" class="col-sm-1 control-label">名称</label>
     4         <div class="col-sm-6">
     5             <input type="text" class="form-control" id="input_name" name="name" required="required">
     6         </div>
     7     </div>
     8     <div class="form-group">
     9         <label for="input_name" class="col-sm-1 control-label">价格</label>
    10         <div class="col-sm-6">
    11             <input type="text" class="form-control" id="input_name" name="price">
    12         </div>
    13     </div>
    14     <div class="form-group">
    15         <label for="input_name" class="col-sm-1 control-label">介绍</label>
    16         <div class="col-sm-6">
    17             <input type="text" class="form-control" id="input_name" name="intro">
    18         </div>
    19     </div>
    20     <div class="form-group">
    21         <label for="input_name" class="col-sm-1 control-label">库存</label>
    22         <div class="col-sm-6">
    23             <input type="text" class="form-control" id="input_name" name="stock">
    24         </div>
    25     </div>
    26     <div class="form-group">
    27         <label for="input_file" class="col-sm-1 control-label">封面图片</label>
    28         <div class="col-sm-6">
    29             <input type="file" name="cover" id="input_file" required="required">推荐尺寸: 500 * 500
    30         </div>
    31     </div>
    32     <div class="form-group">
    33         <label for="input_file" class="col-sm-1 control-label">详情图片1</label>
    34         <div class="col-sm-6">
    35             <input type="file" name="image1" id="input_file" required="required">推荐尺寸: 500 * 500
    36         </div>
    37     </div>
    38     <div class="form-group">
    39         <label for="input_file" class="col-sm-1 control-label">详情图片2</label>
    40         <div class="col-sm-6">
    41             <input type="file" name="image2" id="input_file" required="required">推荐尺寸: 500 * 500
    42         </div>
    43     </div>
    44     <div class="form-group">
    45         <label for="select_topic" class="col-sm-1 control-label">类目</label>
    46         <div class="col-sm-6">
    47             <select class="form-control" id="select_topic" name="good.type.id">
    48                 <c:forEach items="${typeList }" var="t">
    49                     <option value="${t.id }">${t.name }</option>
    50                 </c:forEach>
    51             </select>
    52         </div>
    53     </div>
    54     <div class="form-group">
    55         <div class="col-sm-offset-1 col-sm-10">
    56             <button type="submit" class="btn btn-success">提交保存</button>
    57         </div>
    58     </div>
    59 </form>
    My Code

    使用multipart/form-data会有一个boundary属性,来用将提交的表单数据进行分隔,以用来让服务器知道哪个是我们上传的资源,哪个是普通的表单数据。

    1 <form action="adctionServlet" method="post" enctype="enctype=multipart/form-data">
    2     描述:<input type="text" name=""/><br/>
    3     图片:<input type="file" name="image"/><br/>
    4     <input type="submit" value="上传"/>
    5 </form>

    2)服务器端

    使用commons-fileupload进行处理上传内容。

     1 try {
     2     DiskFileItemFactory factory = new DiskFileItemFactory();
     3     ServletFileUpload upload = new ServletFileUpload(factory);
     4 
     5     List<FileItem> list = upload.parseRequest(request);
     6     Goods goods = new Goods();
     7     for (FileItem item : list) {
     8 
     9         // 判断itme;true-为普通输入项,false-为文件
    10         if (item.isFormField()) {
    11 
    12             // 设置商品属性
    13             switch (item.getFieldName()) {
    14             case "name":
    15                 goods.setName(item.getString("utf-8"));
    16                 break;
    17             case "price":
    18                 goods.setPrice(Float.parseFloat(item.getString("utf-8")));
    19                 break;
    20             case "intro":
    21                 goods.setIntro(item.getString("utf-8"));
    22                 break;
    23             case "stock":
    24                 goods.setStock(Integer.parseInt(item.getString("utf-8")));
    25                 break;
    26             case "good.type.id":
    27                 goods.setTypeid(Integer.parseInt(item.getString("utf-8")));
    28                 break;
    29             }
    30         } else {
    31 
    32             if (item.getInputStream().available() <= 0) {
    33                 continue;
    34             }
    35             // 1-获取文件名;2-获取后缀名;3-根据毫秒值设置新的文件名;4-创建保存路径
    36             String fileName = item.getName();
    37             fileName = fileName.substring(fileName.indexOf("."));
    38             fileName = "/" + System.currentTimeMillis() + fileName;
    39             String path = this.getServletContext().getRealPath("/picture") + fileName;
    40 
    41             // 读取上传
    42             InputStream in = item.getInputStream();
    43             FileOutputStream out = new FileOutputStream(path);
    44             byte[] buff = new byte[1024];
    45             int len = 0;
    46             while ((len = in.read(buff)) > 0) {
    47                 out.write(buff, 0, len);
    48             }
    49             in.close();
    50             out.close();
    51 
    52             // 设置商品属性
    53             switch (item.getFieldName()) {
    54             case "cover":
    55                 goods.setCover("/picture" + fileName);
    56                 break;
    57             case "image1":
    58                 goods.setImage1("/picture" + fileName);
    59                 break;
    60             case "image2":
    61                 goods.setImage2("/picture" + fileName);
    62                 break;
    63             }
    64         }
    65         item.delete();
    66     }
    67 } catch (FileUploadException e) {
    68     e.printStackTrace();
    69 }    
    My Code

    上传的各种问题:

    1,上传文件名乱码问题:使用servletFileUpload.setHeaderEncoding("UTF-8");或者request.setCharacterEncoding("UTF-8")都可以。

    2,表单内容乱码问题:使用getString("utf-8")即可,也就是在获取内容时,就可以设置码表。

    3,上传文件同名问题:使用UUID.randomUUID().toString().replace("-", "").获得一个独一无二的32位数字。

    4,使用FileUtils.copyInputStreamToFile(is, file);来将内容输出到指定路径文件中去,mkdirs() 自动创建目录

     1 public void doGet(HttpServletRequest request, HttpServletResponse response)
     2         throws ServletException, IOException {
     3     try {
     4         
     5         //0.5 检查是否支持文件上传  ,检查请求头Content-Type : multipart/form-data 
     6         if(!ServletFileUpload.isMultipartContent(request)){
     7             throw new RuntimeException("不要得瑟,没用");
     8         }
     9         
    10         //1 工厂
    11         DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();
    12         // 1.1 设置是否生产临时文件临界值。大于2M生产临时文件。保证:上传数据完整性。
    13         fileItemFactory.setSizeThreshold(1024 * 1024 * 2);  //2MB
    14         // 1.2 设置临时文件存放位置
    15         // * 临时文件扩展名  *.tmp  ,临时文件可以任意删除。
    16         String tempDir = this.getServletContext().getRealPath("/temp");
    17         fileItemFactory.setRepository(new File(tempDir));
    18         
    19         //2 核心类
    20         ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
    21         // 2.1 如果使用无参构造  ServletFileUpload() ,手动设置工厂
    22         //servletFileUpload.setFileItemFactory(fileItemFactory);
    23         // 2.2 单个上传文件大小
    24         //servletFileUpload.setFileSizeMax(1024*1024 * 2);  //2M
    25         // 2.3 整个上传文件总大小
    26         //servletFileUpload.setSizeMax(1024*1024*10);        //10M
    27         // 2.4 设置上传文件名的乱码
    28         // * 首先使用  setHeaderEncoding 设置编码
    29         // * 如果没有设置将使用请求编码 request.setCharacterEncoding("UTF-8")
    30         // * 以上都没有设置,将使用平台默认编码
    31         servletFileUpload.setHeaderEncoding("UTF-8");
    32         // 2.5 上传文件进度,提供监听器进行监听。
    33         servletFileUpload.setProgressListener(new MyProgressListener());
    34         
    35         
    36         //3 解析request  ,List存放 FileItem (表单元素的封装对象,一个<input>对应一个对象)
    37         List<FileItem> list = servletFileUpload.parseRequest(request);
    38         
    39         //4 遍历集合获得数据
    40         for (FileItem fileItem : list) {
    41             // 判断
    42             if(fileItem.isFormField()){
    43                 // 5 是否为表单字段(普通表单元素)
    44                 //5.1.表单字段名称
    45                 String fieldName = fileItem.getFieldName();
    46                 System.out.println(fieldName);
    47                 //5.2.表单字段值 , 解决普通表单内容的乱码
    48                 String fieldValue = fileItem.getString("UTF-8");
    49                 System.out.println(fieldValue);
    50             } else {
    51                 //6 上传字段(上传表单元素)
    52                 //6.1.表单字段名称  fileItem.getFieldName();
    53                 //6.2.上传文件名
    54                 String fileName = fileItem.getName();
    55                 // * 兼容浏览器, IE : C:UsersliangtongDesktopabc.txt  ; 其他浏览器 : abc.txt
    56                 fileName = fileName.substring(fileName.lastIndexOf("\") + 1);
    57                 // * 文件重名
    58                 fileName = UUID.randomUUID().toString().replace("-", "") + fileName;
    59                 // * 单个文件夹文件个数过多?
    60                 String subDir = StringUtils.getDir(fileName);
    61                 
    62                 System.out.println(fileName);
    63                 //6.3.上传内容
    64                 InputStream is = fileItem.getInputStream();
    65                 String parentDir = this.getServletContext().getRealPath("/WEB-INF/upload");
    66                 File file = new File(parentDir + subDir,fileName);
    67 
    68                 // 将指定流 写入 到 指定文件中  -- mkdirs() 自动创建目录
    69                 FileUtils.copyInputStreamToFile(is, file);
    70                 
    71                 //7删除临时文件
    72                 fileItem.delete();
    73             }
    74         }
    75         
    76     } catch (Exception e) {
    77         e.printStackTrace();
    78         
    79         throw new RuntimeException(e);
    80         
    81     }
    82 }

    总结上传

    其实理解了也不是很难,就是上传文件后的处理比较麻烦,各种小问题,存储过程最为麻烦。

    1、创建工厂类

    2、使用核心类,

    3、解析request请求,

    4、遍历请求体的内容,将上传内容和普通表单内容都获取出来

    5、获取到上传内容时,对其存储位置进行设置

    @部分转载自:https://www.cnblogs.com/whgk/p/6479405.html

  • 相关阅读:
    模板
    模板
    模板
    模板
    2017-2018 ACM-ICPC Asia Tsukuba Regional Contest
    牛客
    软件工程
    Codeforces Round 696(Div.2)
    Atcoder ARC111 contest
    Codeforces Educational Round 100(Div.2)
  • 原文地址:https://www.cnblogs.com/Dm920/p/11670243.html
Copyright © 2020-2023  润新知