zoukankan      html  css  js  c++  java
  • 文件上传

    文件上传:
    commons-fileupload-1.2.1.jar
    commons-io-2.0.jar

    一、原理

    文件上传中三个重要的API:
    1.org.apache.commons.fileupload.disk.DiskFileItemFactory: 创建 FileItem 实例的工厂
    三个重要的方法:
    (1)setSizeThreshold(int sizeThreshold):设置阀值的大小,该阀值决定了上传的文件是保存在内存中还是保存在临时文件中。参数为字节。默认值为:Size threshold is 10KB.
    (2)setRepository(File repository):指定上传临时目录,默认值为 System.getProperty("java.io.tmpdir")。
    (3)DiskFileItemFactory(int sizeThreshold, File repository)。

    2.org.apache.commons.fileupload.servlet.ServletFileUpload:处理文件上传的类,将表单的每一个输入项封装为一个 FileItem 对象。
    (1)isMultipartContent(HttpServletRequest request): 判断表单上传类型是否以 "multipart/" 开头。
    (2)ServletFileUpload(FileItemFactory fileItemFactory):构造方法,传入FileItemFactory实例。
    (3)List /* FileItem */ parseRequest(HttpServletRequest request):解析 request 对象,并把表单中的每一个输入项包装成一个 fileItem 项,并返回FileItem的list集合。
    (4)setFileSizeMax(long fileSizeMax):设置单个上传文件的最大值,参数为字节。
    (5)setSizeMax(long sizeMax):设置上传文件总量的最大值
    (5)setHeaderEncoding(java.lang.String encoding):设置编码格式

    3.FileItem: keep their content either in memory,for smaller items, or in a temporary file on disk, for larger items.

    步骤:
    1.创建 DiskFileItemFactory 对象,设置缓冲区大小,和临时文件目录
    2.使用 DiskFileItemFactory 对象创建 ServletFileUpload 对象,判断isMultipartContent(req)并设置上传文件的大小限制。
    3.使用 ServletFileUpload 的 parseRequest(req) 方法,得到保存所有上传对象的 List FileItem 对象。
    4.对 List<FileItem> 对象进行迭代,每迭代一个 FileItem 对象,调用其 isFormField() 方法判断其是否是上传文件。
    5.若是上传文件,通过 FileItem 的 getFieldName() 获取字段名,通过 getName() 获取文件名这里需要注意一个问题。
    需要注意的问题:

    通过 FileItem 的 getName() 方法获取到的是:
    IE下:
    C:UserssovlerpengDesktop2016-06-06_172310.png
    Chrome下:
    2016-06-06_172310.png

    需要对IE下的文件名进行处理。
    6.调用 FileItem 的 write(File file) 方法,将缓存的文件写到真实的文件目录里。

    细节问题:
    1.当上传文件超出设置的阀值大小时,Commons-fileupload 将使用临时文件存储上传数据。
    2.文件位置:为防止上传相同文件名的文件,而导致文件被覆盖,所以应该保证上传的文件具有唯一名。
    3.为防止单个目录下文件过多,影响文件读写速度,处理上传文件的类型或时间或其他,生成不同的目录,将文件分散存储。

    4.文件上传速度
    (1) ProgressListener显示上传进度
    ProgressListenerprogressListener = new ProgressListener() {
    public void update(longpBytesRead, long pContentLength, int pItems) {
    System.out.println("到现在为止, " + pBytesRead + " 字节已上传,总大小为 " + pContentLength);
    }
    };
    upload.setProgressListener(progressListener);
    (2)以KB为单位显示上传进度
    long temp = -1; //temp注意设置为类变量
    long ctemp =pBytesRead /1024;
    if (mBytes ==ctemp)
    return;
    temp = mBytes

    5.
    1字节=8位
    1kb = 1024字节
    1mb = 1024kb
    1gb = 1024mb
    1tb = 1024gb

    例子:

    public class UploadUtils {
      public static <T> void doUpload(UploadCondition condition,HttpServletRequest request, T target) throws FileSizeLimitExceededException,SizeLimitExceededException,FileTypeException,Exception {
        FileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);
        upload.setSizeMax(condition.getTotalSize());
        upload.setFileSizeMax(condition.getFileSize());
        List<FileItem> items = upload.parseRequest(request);
        for (FileItem fileItem : items) {
          if(fileItem.isFormField()) {
            String name = fileItem.getFieldName();
            String value = fileItem.getString("UTF-8");
            BeanUtils.copyProperty(target, name, value);
          }else{
            String fieldName = fileItem.getFieldName();
            String fileName = fileItem.getName();
            String contentType = fileItem.getContentType();
          if(!condition.getAllowedTypes().contains(contentType)) {
            throw new FileTypeException("文件类型不合法");
          }
          if(fileName.contains("\")) {
            int lastIndexOf = fileName.lastIndexOf("\");
            fileName = fileName.substring(lastIndexOf+1);
          }
          fileName = System.currentTimeMillis() + fileName;
          BeanUtils.copyProperty(target, fieldName, condition.getFolder() + "/" + fileName);
          String realPath = request.getSession().getServletContext().getRealPath("/"+condition.getFolder());
          File file = new File(realPath+"/"+fileName);
          fileItem.write(file);
        }
      }
      }
    }
    UploadUtils.java
    /**
    * 封装文件上传时需要使用的条件数据
    * 为每一个属性指定默认值,如果使用默认值则可以调用无参的构造器创建对象
    *
    */
    public class UploadCondition {
        private long fileSize = 100*1024;
        private long totalSize = 200*1024;
        private List<String> allowedTypes =             Arrays.asList("image/jpeg","image/gif");
        private String folder = "upload";
        public UploadCondition() {
        }
      public UploadCondition(Long fileSize, Long totalSize, List<String> allowedTypes, String folder) {
        super();
        if(fileSize != null) {
          this.fileSize = fileSize;
        }
        if(totalSize != null) {
          this.totalSize = totalSize;
        }
        if(allowedTypes != null) {
          this.allowedTypes = allowedTypes;
        }
        if(folder != null) {
          this.folder = folder;
        }
      }
    
      public long getFileSize() {
        return fileSize;
      }
    
      public void setFileSize(long fileSize) {
        this.fileSize = fileSize;
      }
    
      public long getTotalSize() {
        return totalSize;
      }
    
      public void setTotalSize(long totalSize) {
        this.totalSize = totalSize;
      }
    
      public List<String> getAllowedTypes() {
        return allowedTypes;
      }
    
      public void setAllowedTypes(List<String> allowedTypes) {
        this.allowedTypes = allowedTypes;
      }
    
      public String getFolder() {
        return folder;
      }
    
      public void setFolder(String folder) {
        this.folder = folder;
      }
    }
    UploadCondition.java
    public class FileTypeException extends RuntimeException{
        public FileTypeException() {
            super();
        }
        public FileTypeException(String message, Throwable cause,
            boolean enableSuppression, boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
        }
        public FileTypeException(String message, Throwable cause) {
            super(message, cause);
        }
        public FileTypeException(String message) {
            super(message);
        }
        public FileTypeException(Throwable cause) {
            super(cause);
        }
    }
    FileTypeException.java
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      User user = new User();
      UploadCondition condition = new UploadCondition();
      condition.setTotalSize(1000*1024);
      try {
        UploadUtils.doUpload(condition, request, user);
      } catch (FileSizeLimitExceededException e) {
        e.printStackTrace();
        request.setAttribute("message", "单个文件大小不要超过:"+condition.getFileSize());
        request.getRequestDispatcher("/regist.jsp").forward(request, response);
        return ;
      } catch (SizeLimitExceededException e) {
        e.printStackTrace();
        request.setAttribute("message", "总文件大小不要超过:"+condition.getTotalSize());
        request.getRequestDispatcher("/regist.jsp").forward(request, response);
        return ;
    
      } catch (FileTypeException e) {
        e.printStackTrace();
        request.setAttribute("message", e.getMessage());
        request.getRequestDispatcher("/regist.jsp").forward(request, response);
        return ;
    
      } catch (Exception e) {
        e.printStackTrace();
      }
    
      request.setAttribute("user", user);
      request.getRequestDispatcher("/result.jsp").forward(request, response);
    
    }
    UploadServlet.java

    二、Struts2 

    1.Struts2 的文件上传,也是使用了 commons-fileupload-1.2.1.jar commons-io-2.0.jar 这两个包。只不过通过 FileUploadInterceptor 对其进行了进一步的封装,使其能更加简单,更加快速的开发。

    2.具体内容可以参看 FileUploadInterceptor Javadoc 。

    3.使用步骤:

    (1)表单要求:enctype="multipart/form-data" method="post" type="file" 的表单标签

    如:

    <s:form action="/myUpload" enctype="multipart/form-data" method="post">
        <s:file name="my" label="文件上传"/>
        <s:textfield name="fileDesc" label="文件描述"/>
        <s:submit value="上传"/>
    </s:form>

    (2)目标 Action 类:

    [File Name] : File - the actual File

    如:my → C:Usersxzs.IntelliJIdea15system omcatUnnamed_struts2_fileuploadworkCatalinalocalhoststruts_fileuploadupload__94063e_155ddebc370__7fd0_00000000.tmp

    [File Name]ContentType : String - the content type of the file

    如:myContentType → image/jpeg

    [File Name]FileName : String - the actual name of the file uploaded (not the HTML name)

    如:myFileName → 70f8723a6fcbac4f377391b1ce679c7b.jpeg

    并提供对应的 getXxx() 和 setXxx() 方法。

    (3)具体操作:

    将 my 通过文件输入流的方式读入。

    根据上传到目录在项目中虚拟路径通过 servletContext 获取到在服务器中的绝对路径,再加上当前时间的时间戳,再加上文件名(myFileName)组合成唯一值,作为文件输出流的参数。

    从输入流中读取数据到输出流。

    关闭输出流,输入流。

    FileInputStream in = new FileInputStream(my);
    String realPath = context.getRealPath("/WEB-INF/upload");
    String targetPath = realPath + "/" + System.currentTimeMillis() + myFileName;
    FileOutputStream out = new FileOutputStream(targetPath);
    byte[] b = new byte[1024];
    int len = 0;
    while((len = in.read(b)) != -1) {
        out.write(b, 0, len);
    }
    out.close();
    in.close();    

    4.多文件上传

    (1)多文件上传和单文件上传类似,唯一的区别就是,需要的三个参数改为 List 类型即可,然后对 List 进行遍历,进行文件流的操作。

    (2)具体操作:

    <s:form action="/myFileUpload" enctype="multipart/form-data" method="post">
                <s:file name="logo" label="文件上传"/>
                <s:file name="logo" label="文件上传"/>
                <s:textfield name="fileDesc" label="文件描述"/>
                <s:submit value="上传"/>
    </s:form>
    form
    /**
     * @author solverpeng
     * @create 2016-07-12-15:33
     */
    public class MultipartUpload extends ActionSupport implements ServletContextAware {
        private List<File> logo;
        private List<String> logoContentType;
        private List<String> logoFileName;
    
        private ServletContext context;
    
        private String fileDesc;
    
        public List<File> getLogo() {
            return logo;
        }
    
        public void setLogo(List<File> logo) {
            this.logo = logo;
        }
    
        public List<String> getLogoContentType() {
            return logoContentType;
        }
    
        public void setLogoContentType(List<String> logoContentType) {
            this.logoContentType = logoContentType;
        }
    
        public List<String> getLogoFileName() {
            return logoFileName;
        }
    
        public void setLogoFileName(List<String> logoFileName) {
            this.logoFileName = logoFileName;
        }
    
        public String getFileDesc() {
            return fileDesc;
        }
    
        public void setFileDesc(String fileDesc) {
            this.fileDesc = fileDesc;
        }
    
        @Override
        public String execute() throws Exception {
            FileInputStream in = null;
            FileOutputStream out = null;
            String realPath = context.getRealPath("/WEB-INF/upload") + "/" + System.currentTimeMillis();
            String targetPath = "";
            for(int i = 0; i < logo.size(); i++) {
                in = new FileInputStream(logo.get(i));
                targetPath = realPath + logoFileName.get(i);
                out = new FileOutputStream(targetPath);
    
                byte[] b = new byte[1024];
                int len = 0;
                while((len = in.read(b)) != -1) {
                    out.write(b, 0, len);
                }
    
                out.close();
                in.close();
            }
    
            return SUCCESS;
        }
    
        @Override
        public void setServletContext(ServletContext context) {
            this.context = context;
        }
    }
    MultipartUpload

    5.类型限制:文件类型限制对应于 org.apache.struts2.interceptor.FileUploadInterceptor 定义的几个属性,Struts2 中对文件类型的限制即对拦截器中对应属性的设置。

    (1)总文件大小限制:默认上传文件总大小是在 default.properties 中定义的,默认为2M。如果要更改的话,通过修改常量的方式来修改总文件大小。如:

    <constant name="struts.multipart.maxSize" value="5242880"/><!-- 改为5M -->

    (2)单个文件大小限制:maximumSize

    (3)文件类型限制 :allowedTypes,allowedExtensions

    如:

    <interceptors>
        <interceptor-stack name="myStack">
            <interceptor-ref name="defaultStack">
                <param name="fileUpload.maximumSize">5242880</param> <!-- 单个文件总大小 -->
                    <param name="fileUpload.allowedTypes">image/jpeg,application/pdf</param> <!-- 允许的类型 -->
                    <param name="fileUpload.allowedExtensions">pdf</param><!-- 运行的后缀 -->
            </interceptor-ref>
        </interceptor-stack>
    </interceptors>
    <default-interceptor-ref name="myStack"/>

    6.自定义错误消息

    (1)默认的文件上传错误消息存放在 D:workspaceidea ucsoftstruts2_fileuploadwebappWEB-INFlibstruts2-core-2.3.15.3.jarstruts-messages.properties 下

    (2)自定义错误消息:

    添加国际化资源文件:i18n.properties,指定国际化资源文件基名:<constant name="struts.custom.i18n.resources" value="i18n"/>

    struts.messages.error.file.too.large=The file is to large to be uploaded: {0} "{1}" "{2}" {3}
    struts.messages.error.content.type.not.allowed=Content-Type not allowed: {0} "{1}" "{2}" {3}
    struts.messages.error.file.extension.not.allowed=File extension not allowed: {0} "{1}" "{2}" {3}
    
    struts.messages.upload.error.SizeLimitExceededException=Request exceeded allowed size limit! Max size allowed is: {0} but request was: {1}!
    struts.messages.upload.error.IOException=Error uploading: {0}!

    占位符介绍:

    0:当前文件上传域的name值
    1:文件名
    2:临时文件名
    3:文件类型

    三、SpringMVC 文件上传

    1.SpringMVC对文件上传提供了直接的支持,使用 MultipartResolver 实现,具体实现类:CommonsMultipartResolver。

    2.Spring 上下文默认没有装配 MultipartResolver,若想支持文件上传,需要装配 MultipartResolver。

    <bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
        <property name="defaultEncoding" value="UTF-8"/>
        <property name="maxUploadSize" value="5343880"/>
    </bean>

    当然需要导入:

    commons-fileupload-1.2.1.jar
    commons-io-2.0.jar

    这两个包。

    3.单文件上传

    表单:

    <form action="testUpload" enctype="multipart/form-data" method="post">
        Desc: <input type="text" name="desc"/>
        File: <input type="file" name="file"/>
        <input type="submit" value="Submit" />
    </form>

    handler 方法:

    @RequestMapping("/testUpload")
    public String upload(@RequestParam("desc") String desc, @RequestParam("file")MultipartFile file, HttpServletRequest request) throws IOException {
        System.out.println(desc);
        uploadFile(file, request);
        return "success";
    }
    private void uploadFile(MultipartFile file, HttpServletRequest request) throws IOException {
        if(!file.isEmpty()) {
            String targetPath = request.getSession().getServletContext().getRealPath("/")+ "upload/" + file.getOriginalFilename();
            file.transferTo(new File(targetPath));
        }
    }

    4.多文件上传

    表单:

    <form action="testUpload2" enctype="multipart/form-data" method="post">
        Desc: <input type="text" name="desc"/>
        File1: <input type="file" name="file"/>
        File2: <input type="file" name="file"/>
        File3: <input type="file" name="file"/>
        <input type="submit" name="Submit">
    </form>

    handler 方法:

    @RequestMapping("/testUpload2")
    public String upload(@RequestParam("desc") String desc, @RequestParam("file") MultipartFile[] files, HttpServletRequest request) throws IOException {
        System.out.println(desc);
        for(MultipartFile file : files) {
            uploadFile(file, request);
        }
        return "success";
    }

    5.对于类型限制,我这里提供两种思路。

    (1)通过拦截器的方式来过滤,但是不够灵活。

    (2)通过读取表中限制类型的方式。

    (3)通过配置文件的方式。

    6.在生产环境中

    在生产环境中,一般会新增一个文件类,用来保存上传成功后的一些信息,如路径、文件名等,保存在数据表中,在使用的时候就非常方便了。

  • 相关阅读:
    NOI模拟赛 6.20
    NOI模拟赛 6.17
    NOI模拟赛 6.16
    计算几何学习笔记
    NOI(p)模拟赛 5.30
    NOI模拟赛 5.26
    [AGC022E] Median Replace 题解
    看完魔圆之后的一点感想(大概
    OI学习日志 11月份
    2021 CSP-S 游记
  • 原文地址:https://www.cnblogs.com/solverpeng/p/5613727.html
Copyright © 2011-2022 走看看