javaWeb文件上传与下载

本文详细介绍了JavaWeb中文件上传与下载的原理和实战步骤。在文件下载部分,讲解了如何设置下载路径、文件名以及利用缓冲区实现文件下载。文件上传部分讨论了所需的依赖、关键类的使用,如`Commons IO`库,以及前端限制文件大小的重要性,因为后端限制可能导致请求崩溃。实战部分包括项目结构、依赖注入、Servlet编写等,同时提到了在设置上传大小限制时遇到的问题及其解决困扰。

javaWeb文件上传与下载

理论(原理分析):

文件下载

  1. 要获取下载文件的路径
  2. 下载的文件名(显示给客户端的文件名)
  3. 设置想办法让浏览器能够支持下载我们需要的东西(attachment;filename
  4. 创建文件输入流
  5. 创建缓冲区
  6. 获取OutputStream对象
  7. FileOutputStream流写入到buffer缓冲区
  8. 使用OutputStream将缓冲区中的数据输出到客户端!
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 1. 要获取下载文件的路径
    String realPath = "F:\\班级管理\\西开【19525】\\2、代码\\JavaWeb\\javaweb-02-servlet\\response\\target\\classes\\秦疆.png";
    System.out.println("下载文件的路径:"+realPath);
    // 2. 下载的文件名是啥?
    String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
    // 3. 设置想办法让浏览器能够支持(Content-Disposition)下载我们需要的东西,中文文件名URLEncoder.encode编码,否则有可能乱码
    resp.setHeader("Content-Disposition","attachment;filename="+URLEncoder.encode(fileName,"UTF-8"));
    // 4. 获取下载文件的输入流
    FileInputStream in = new FileInputStream(realPath);
    // 5. 创建缓冲区
    int len = 0;
    byte[] buffer = new byte[1024];
    // 6. 获取OutputStream对象
    ServletOutputStream out = resp.getOutputStream();
    // 7. 将FileOutputStream流写入到buffer缓冲区,使用OutputStream将缓冲区中的数据输出到客户端!
    while ((len=in.read(buffer))>0){
        out.write(buffer,0,len);
    }

    in.close();
    out.close();
}

注意:设置头时attachment;filename的分号如果不小心写成冒号,则只会显示图片而不是下载图片

文件上传

需要的依赖包
  1. commons.io

    <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.11.0</version>
    </dependency>
    
  2. commons.fileupload(依赖于``commons.io`)

    <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.4</version>
    </dependency>
    
需要用到的类

ServletFileUpload 负责处理上传文件数据,并将表单每个输入项封装成一个FileItem`对象,在使用`ServletFileUplead`对象解析请求时需要`DiskFileItemFactory`对象。所以我们需要在进行解析工作前构造好`DiskFileItemFactory`对象,通过`ServletFileUpload`对象的构造方法或`setFileItemFactory()方法设置ServletFileUpload对象的fileItemFactory属性

FileItem

在HTML页面input必须要有name<input type="file" name="filename">

表单如果包含一个文件上传输入项的话,这个表单的enctype属性就必须设置为multipart/form-data

<form action="" enctype="multipart/form-data" method="post">

    上传用户<input type="text" name="username"><br/>

    <p><input type="file" name="file1"></p>
    <p><input type="file" name="file2"></p>

    <p><input type="submit"> | <input type="reset"></p>

</form>

常用方法

//isFormField方法用于判断FileItem类地下封装的数据是一个普通文本表单
//还是一个文件表单,如果是普通表单字段则返回true,否则返回false
boolean isFormField();

//getFiledName()方法用于返回表单标签name属性的值。
String getFieldName();
//getString()方法用于将FileItem对象中保存的数据流内容以一个字符串返回
String getString();

//getName()方法用于获得文件上传字段中的文件名
String getName();

//以流的形式返回上传文件的数据内容。
InputStream getInputStream();

//delete方法用来清空FileItem类对象中存放的主体内容
//如果主体内容被保存在临时文件中,delete方法将删除该临时文件。
void delete();

ServletFileUpload

ServletFileUpload负责处理上传的文件数据,并架构表单中每个输入项封装成一个FileItem对象中,使用其**parseRequest(HttpServletRequest r)**方法可以将通过表单中每一个HTML标签提交的数据封装成一个FileItem对象,然后以List列表的形式返回。使用该方法处理上传文件简单易用。

package com.example.file;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;

@WebServlet(name = "FileServlet", value = "/FileServlet")
public class FileServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {


    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {


        //判断上传的文件时普通表单还是带文件的表单
        if (!ServletFileUpload.isMultipartContent(request)) {
            return;//终止方法运行,说明这是个普通表单,直接返回
        }
        //创建上传文件的保存路径 建议保存到WEB-INF路径下 安全 用户无法直接访问上传的文件
        String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
        File uploadFile = new File(uploadPath);
        if (!uploadFile.exists()) {
            uploadFile.mkdir();//创建这个目录
        }
        //缓存,临时文件
        String tmpPath = this.getServletContext().getRealPath("/WEB-INF/tmp");
        File tmpFile = new File(tmpPath);
        if (!tmpFile.exists()) {
            tmpFile.mkdir();//创建这个临时目录
        }
        //处理上传的文件
        /*
        ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象
        在使用ServletFileUpload对象解析请求时需要DiskFileItemFactory对象
        所以 Wimbledon需要在进行解析工作前构造好DiskFileItemFactory对象
        通过ServletFileUpload对象的构造方法或setFileItemFactory()方法设置ServletFileUpload对象的fileItemFactory属性
         */

        DiskFileItemFactory factory = getDiskFileItemFactory(tmpFile);
        ServletFileUpload servletFileUpload = getServletFileUpload(factory);
        String result = extracted(request, uploadPath, servletFileUpload);
        request.setAttribute("result",result);
        System.out.println(result);
        request.getRequestDispatcher("info.jsp").forward(request,response);



    }

    private String extracted(HttpServletRequest request, String uploadPath, ServletFileUpload servletFileUpload){
        StringBuilder msg = new StringBuilder();
        try{
            //3.处理上传的文件
            //吧前端请求解析 封装成一个FileItem对象
            List<FileItem> fileItems = servletFileUpload.parseRequest(request);
            for (FileItem fileItem : fileItems) {
                String name;
                if(fileItem.isFormField()){
                    //getFileName指的是前端表单控件Name
                    name = fileItem.getFieldName();
                    String value = fileItem.getString("UTF-8");//处理乱码
                    System.out.println(name+":"+value);
                }else {//文件
                    //----------------处理文件-----------------//
                    //获取文件全名
                    name = fileItem.getName();
                    //判断文件大小
                    if(fileItem.getSize() >= 1024 * 1024 * 10){
                        msg.append(name).append(":").append("超过内存限制,拒绝传输<br>");
                        fileItem.delete();//上传成功,清除临时文件
                        continue;
                    }
                    //可能存在文件名不合法的情况
                    if(name == null|| name.trim().equals("")){
                        fileItem.delete();//上传成功,清除临时文件
                        continue;
                    }
                    //获取上传的文件名
                    String fileName = name.substring(name.lastIndexOf('/') + 1);
                    //获取文件后缀
                    String fileExtName = name.substring(name.lastIndexOf('.') + 1);
                /*
                     如果后缀名不是需要的则直接return不处理
                     告诉用户文件类型不正确
                 */

                    //可以使用UUID(唯一识别的通识码)保证文件名唯一
                    //UUID.randomUUID(),随机产生一个识别的通用码

                    //网络传输中的东西都需要序列化
                    //实体类,如果想要在多个计算机上运行,需要传输(把对象都序列化)
                    //implement Serializable : 标记接口,JVM---> Java栈  本地方法栈 native -->C++
                    String uuidPath = UUID.randomUUID().toString();


                    //----------------存放地址-----------------//
                    //存放位置 uploadPath
                    //文件真是存在的路径 realPath
                    String realPath = uploadPath +"/"+uuidPath;
                    //给每个文件创建一个对应的文件夹
                    File realPathFile = new File(realPath);
                    if(!realPathFile.exists()){
                        realPathFile.mkdir();
                    }


                    //----------------文件传输-----------------//
                    //获得文件上传的流
                    InputStream is = fileItem.getInputStream();

                    //创建一个文件输出流
                    FileOutputStream fos = new FileOutputStream(realPath + "/" + name);

                    //创建一个缓冲区
                    byte[] buffer = new byte[1024 * 1024];

                    //判断是否读取完毕
                    int len =0;
                    //如果大于0说明还存在数据
                    while ((len=is.read(buffer))>0){
                        fos.write(buffer,0,len);
                    }

                    //关闭流
                    fos.close();
                    is.close();

                    msg.append(name).append(":上传成功<br>");

                    fileItem.delete();//上传成功,清除临时文件
                }
            }

        }catch (Exception e){
            e.printStackTrace();
            return "上传异常";
        }

        return msg.toString();
    }

    private ServletFileUpload getServletFileUpload(DiskFileItemFactory factory) {
        //2.获取ServletFileUpload
        ServletFileUpload servletFileUpload = new ServletFileUpload(factory);
        //监听文件上传进度
//        servletFileUpload.setProgressListener((l, all, i) -> System.out.println("总大小:" + all + "\t已上传:" + l));
        //处理乱码问题
        servletFileUpload.setHeaderEncoding("UTF-8");
        //设置单个文件的最大值
//        servletFileUpload.setFileSizeMax(1024 * 1024 * 10);//10M
        //设置总共能够上传文件的大小
//        servletFileUpload.setSizeMax(1024 * 1024 * 10 * 2);
        /*
            如果设置了大小则如果出现超过大小的情况 则直接异常
         */
        return servletFileUpload;
    }

    private DiskFileItemFactory getDiskFileItemFactory(File dir) {
        //1.创建DiskFileItemFactory对象,处理文件上传路径或者大小限制
        DiskFileItemFactory factory = new DiskFileItemFactory();
        //通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区时,将他放到临时文件中
        if(dir!=null){
            factory.setSizeThreshold(1024 * 1024);//缓存区大小为1M
            factory.setRepository(dir);
        }
        return factory;
    }
}

不足:

在上述代码中并没有真正限制大小,而是允许文件上传到tmp文件夹中后再进行判断大小是否超额。目前我只能在前端进行判断大小进行限制,后端进行限制会在转换的时候异常,然而该异常又会导致request奔溃,前端收不到对应回复。

实战(项目实战):

项目一览:

  1. 项目目录
    在这里插入图片描述

  2. 项目展示

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

初始化项目

  1. 我使用的编辑器:IDEA2021.2

  2. 项目创建

    在这里插入图片描述
    在这里插入图片描述

依赖注入(pom.xml)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>File</artifactId>
  <version>1.0-SNAPSHOT</version>
  <name>File</name>
  <packaging>war</packaging>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.target>1.8</maven.compiler.target>
    <maven.compiler.source>1.8</maven.compiler.source>
      <junit.version>5.7.1</junit.version>
      </properties>

  <dependencies>
                                                                        <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>                                                                        
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-engine</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
      <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
      <dependency>
          <groupId>commons-io</groupId>
          <artifactId>commons-io</artifactId>
          <version>2.11.0</version>
      </dependency>
      <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
      <dependency>
          <groupId>commons-fileupload</groupId>
          <artifactId>commons-fileupload</artifactId>
          <version>1.4</version>
      </dependency>
      <!-- JSTL表达式的依赖 -->
      <dependency>
          <groupId>javax.servlet.jsp.jstl</groupId>
          <artifactId>jstl-api</artifactId>
          <version>1.2</version>
      </dependency>
      <!-- standard标签库 -->
      <dependency>
          <groupId>taglibs</groupId>
          <artifactId>standard</artifactId>
          <version>1.1.2</version>
      </dependency>


  </dependencies>

  <build>
     <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.3.1</version>
      </plugin>
     </plugins>
  </build>
</project>

编写(导入)静态资源

  1. JSP

    1. upload.jsp

      <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
      <!DOCTYPE html>
      <html>
      <head>
        <title>文件上传</title>
        <link type="text/css" rel="stylesheet" href="${pageContext.request.contextPath}/css/css.css"/>
        <link type="text/css" rel="stylesheet" href="${pageContext.request.contextPath}/css/css2.css"/>
      </head>
      <body>
      
      <div class="form">
      
        <form id="fileForm" action="${pageContext.request.contextPath}/UploadServlet" enctype="multipart/form-data" method="post">
      
          上传用户<input type="text" name="username"><br>
          <br>
          <div>
            <label for="files">选择文件上传(可多选)</label>
            <input type="file" id="files" name="files" multiple="" style="opacity: 0;">
          </div>
          <br>
          <div class="preview">
            <p>当前还没有选中的文件</p>
          </div>
          <br>
          <p>
            <input id="submitBtn" type="button" value="上传"> | <input type="reset" id="resetBtn">
          </p>
        </form>
            <a href="${pageContext.request.contextPath }/DownloadServlet?method=getFileNameList"><button>去下载文件</button></a>
      </div>
      
      </body>
      
      <script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.8.3.min.js"></script>
      <script type="text/javascript" src="${pageContext.request.contextPath }/js/upload.js"></script>
      
      </html>
      
    2. info.jsp

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
          <title>上传文件</title>
          <link type="text/css" rel="stylesheet" href="${pageContext.request.contextPath}/css/css.css"/>
          <link type="text/css" rel="stylesheet" href="${pageContext.request.contextPath}/css/css2.css"/>
      </head>
      <body>
      
      <div class="form">
          <h2>上传状态:</h2>
          <div>${result}</div>
          <a href="${pageContext.request.contextPath }/upload.jsp"><button>去上传文件</button></a>
          <a href="${pageContext.request.contextPath }/DownloadServlet?method=getFileNameList"><button>去下载文件</button></a>
      </div>
      
      </body>
      
      </html>
      
    3. download.jsp

      <%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8"%>
      <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
      <!DOCTYPE HTML>
      <html>
      <head>
          <title>下载文件</title>
          <link type="text/css" rel="stylesheet" href="${pageContext.request.contextPath}/css/css.css"/>
          <link type="text/css" rel="stylesheet" href="${pageContext.request.contextPath}/css/css2.css"/>
      </head>
      
      <body>
      
      <div class="form">
          <!-- 遍历Map集合 -->
          <c:forEach var="me" items="${fileNameMap}">
              <c:url value="/DownloadServlet" var="downurl">
                  <c:param name="path" value="${me.key}"></c:param>
                  <c:param name="realName" value="${me.value}"></c:param>
              </c:url>
              <a href="${downurl}&method=downloadFile">[下载]</a>-----<a href="${downurl}&method=previewFile">[预览]</a>${me.value}<br/>
          </c:forEach>
          <a href="${pageContext.request.contextPath }/upload.jsp"><button>去上传文件</button></a>
      </div>
      
      </body>
      </html>
      
  2. CSS

    • css.css

      
      html {
          font-family: sans-serif;
      }
      
      .form {
          width: 580px;
          background: #ccc;
          margin: 0 auto;
          padding: 20px;
          border: 1px solid black;
      }
      
      form ol {
          padding-left: 0;
      }
      
      form li, div > p {
          background: #eee;
          display: flex;
          justify-content: space-between;
          margin-bottom: 10px;
          list-style-type: none;
          border: 1px solid black;
      }
      
      form img {
          height: 64px;
          order: 1;
      }
      
      form p {
          line-height: 32px;
          padding-left: 10px;
      }
      
      form label, form button {
          background-color: #7F9CCB;
          padding: 5px 10px;
          border-radius: 5px;
          border: 1px ridge black;
          font-size: 0.8rem;
          height: auto;
      }
      
      form label:hover, form button:hover {
          background-color: #2D5BA3;
          color: white;
      }
      
      form label:active, form button:active {
          background-color: #0D3F8F;
          color: white;
      }
      
    • css2.css

      body {
          padding: 0;
          margin: 0;
      }
      
      svg:not(:root) {
          display: block;
      }
      
      .playable-code {
          background-color: #f4f7f8;
          border: none;
          border-left: 6px solid #558abb;
          border-width: medium medium medium 6px;
          color: #4d4e53;
          height: 100px;
          width: 90%;
          padding: 10px 10px 0;
      }
      
      .playable-canvas {
          border: 1px solid #4d4e53;
          border-radius: 2px;
      }
      
      .playable-buttons {
          text-align: right;
          width: 90%;
          padding: 5px 10px 5px 26px;
      }
      
      
  3. JavaScript

    • jquery-1.8.3.min.js 这个我就不写进来了,可以到网上下载

    • upload.js

      const inputFiles = document.getElementById('files');
      const preview = document.querySelector('.preview');
      const submitBtn = $("#submitBtn");
      const resetBtn = $("#resetBtn");
      const fileForm = $("#fileForm");
      const maxSize = 1024*1024*10;//10M 单个文件大小限制
      
      inputFiles.style.opacity = 0;
      inputFiles.addEventListener('change', updateImageDisplay);
      
      //可以显示的图片类型
      const fileTypes = [
          'image/jpeg',
          'image/pjpeg',
          'image/png'
      ];
      
      function validFileType(file) {
          return fileTypes.includes(file.type);
      }
      
      function returnFileSize(number) {
          if(number < 1024) {
              return number + 'bytes';
          } else if(number >= 1024 && number < 1048576) {
              return (number/1024).toFixed(1) + 'KB';
          } else if(number >= 1048576) {
              return (number/1048576).toFixed(1) + 'MB';
          }
      }
      
      
      function updateImageDisplay() {
          let isOK = true;
      
          while(preview.firstChild) {
              preview.removeChild(preview.firstChild);
          }
      
          const curFiles = inputFiles.files;
          if(curFiles.length === 0) {
              const para = document.createElement('p');
              para.textContent = '当前还没有选中的文件';
              preview.appendChild(para);
              isOK = false;
          } else {
              const list = document.createElement('ol');
              preview.appendChild(list);
      
              for(const file of curFiles) {
                  const listItem = document.createElement('li');
                  const para = document.createElement('p');
                  para.textContent = "文件名称: "+file.name+", 文件大小: "+returnFileSize(file.size);
      
                  //根据文件大小判断是否超过限制
                  if(file.size >= maxSize){
                      para.style.color = "red";
                      isOK = false;
                  }else {
                      para.style.color = "green";
                  }
      
                  //如果是图片类型就显示图片
                  if(validFileType(file)) {
                      const image = document.createElement('img');
                      image.src = URL.createObjectURL(file);
                      listItem.appendChild(image);
                  }
      
                  listItem.appendChild(para);
      
                  list.appendChild(listItem);
              }
      
          }
          return isOK;
      }
      
      submitBtn.bind("click",function () {
          if(updateImageDisplay()){
              if(confirm("您确定要将这些文件上传吗?")){
                  fileForm.submit();
              }else {
                  alert("未选中文件或者选中的文件不符合上传要求!");
              }
          }
      })
      
      resetBtn.bind("click",function () {
          setTimeout(updateImageDisplay,200);
      })
      

编写Servlet

web.xml

这里没有写Servlet的映射,直接由Servlet中的@WebServlet(name = "---", value = "/---")注释搞定映射。于是只在web.xml中设置了一个欢迎页面。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--设置欢迎页面-->
    <welcome-file-list>
        <welcome-file>upload.jsp</welcome-file>
    </welcome-file-list>

</web-app>
UploadServlet
package com.example.file;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;

@WebServlet(name = "UploadServlet", value = "/UploadServlet")
public class UploadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {


    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {


        //判断上传的文件时普通表单还是带文件的表单
        if (!ServletFileUpload.isMultipartContent(request)) {
            return;//终止方法运行,说明这是个普通表单,直接返回
        }
        //创建上传文件的保存路径 建议保存到WEB-INF路径下 安全 用户无法直接访问上传的文件
        String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
        File uploadFile = new File(uploadPath);
        if (!uploadFile.exists()) {
            uploadFile.mkdir();//创建这个目录
        }
        //缓存,临时文件
        String tmpPath = this.getServletContext().getRealPath("/WEB-INF/tmp");
        File tmpFile = new File(tmpPath);
        if (!tmpFile.exists()) {
            tmpFile.mkdir();//创建这个临时目录
        }
        //处理上传的文件
        /*
        ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象
        在使用ServletFileUpload对象解析请求时需要DiskFileItemFactory对象
        所以 Wimbledon需要在进行解析工作前构造好DiskFileItemFactory对象
        通过ServletFileUpload对象的构造方法或setFileItemFactory()方法设置ServletFileUpload对象的fileItemFactory属性
         */

        DiskFileItemFactory factory = getDiskFileItemFactory(tmpFile);
        ServletFileUpload servletFileUpload = getServletFileUpload(factory);
        String result = extracted(request, uploadPath, servletFileUpload);
        request.setAttribute("result",result);
        System.out.println(result);
        request.getRequestDispatcher("info.jsp").forward(request,response);



    }

    private String extracted(HttpServletRequest request, String uploadPath, ServletFileUpload servletFileUpload){
        StringBuilder msg = new StringBuilder();
        try{
            //3.处理上传的文件
            //吧前端请求解析 封装成一个FileItem对象
            List<FileItem> fileItems = servletFileUpload.parseRequest(request);
            for (FileItem fileItem : fileItems) {
                String name;
                if(fileItem.isFormField()){
                    //getFileName指的是前端表单控件Name
                    name = fileItem.getFieldName();
                    String value = fileItem.getString("UTF-8");//处理乱码
                    System.out.println(name+":"+value);
                }else {//文件
                    //----------------处理文件-----------------//
                    //获取文件全名
                    name = fileItem.getName();
                    //判断文件大小
                    if(fileItem.getSize() >= 1024 * 1024 * 10){
                        msg.append(name).append(":").append("超过内存限制,拒绝传输<br>");
                        fileItem.delete();//上传成功,清除临时文件
                        continue;
                    }
                    //可能存在文件名不合法的情况
                    if(name == null|| name.trim().equals("")){
                        fileItem.delete();//上传成功,清除临时文件
                        continue;
                    }
                    //获取上传的文件名
                    String fileName = name.substring(name.lastIndexOf('/') + 1);
                    //获取文件后缀
                    String fileExtName = name.substring(name.lastIndexOf('.') + 1);
                /*
                     如果后缀名不是需要的则直接return不处理
                     告诉用户文件类型不正确
                 */

                    //可以使用UUID(唯一识别的通识码)保证文件名唯一
                    //UUID.randomUUID(),随机产生一个识别的通用码

                    //网络传输中的东西都需要序列化
                    //实体类,如果想要在多个计算机上运行,需要传输(把对象都序列化)
                    //implement Serializable : 标记接口,JVM---> Java栈  本地方法栈 native -->C++
                    String uuidPath = UUID.randomUUID().toString();


                    //----------------存放地址-----------------//
                    //存放位置 uploadPath
                    //文件真实存在的路径 realPath = uploadPath/uuid_fileName
                    String realPath = uploadPath + "/" +uuidPath + "_" + name;
                    System.out.println(realPath);


                    //----------------文件传输-----------------//
                    //获得文件上传的流
                    InputStream is = fileItem.getInputStream();

                    //创建一个文件输出流
                    FileOutputStream fos = new FileOutputStream(realPath);

                    //创建一个缓冲区
                    byte[] buffer = new byte[1024 * 1024];

                    //判断是否读取完毕
                    int len =0;
                    //如果大于0说明还存在数据
                    while ((len=is.read(buffer))>0){
                        fos.write(buffer,0,len);
                    }

                    //关闭流
                    fos.close();
                    is.close();

                    msg.append(name).append(":上传成功<br>");

                    fileItem.delete();//上传成功,清除临时文件
                }
            }

        }catch (Exception e){
            e.printStackTrace();
            return "上传异常";
        }

        return msg.toString();
    }

    private ServletFileUpload getServletFileUpload(DiskFileItemFactory factory) {
        //2.获取ServletFileUpload
        ServletFileUpload servletFileUpload = new ServletFileUpload(factory);
        //监听文件上传进度
//        servletFileUpload.setProgressListener((l, all, i) -> System.out.println("总大小:" + all + "\t已上传:" + l));
        //处理乱码问题
        servletFileUpload.setHeaderEncoding("UTF-8");
        //设置单个文件的最大值
//        servletFileUpload.setFileSizeMax(1024 * 1024 * 10);//10M
        //设置总共能够上传文件的大小
//        servletFileUpload.setSizeMax(1024 * 1024 * 10 * 2);
        /*
            如果设置了大小则如果出现超过大小的情况 则直接异常
         */
        return servletFileUpload;
    }

    private DiskFileItemFactory getDiskFileItemFactory(File dir) {
        //1.创建DiskFileItemFactory对象,处理文件上传路径或者大小限制
        DiskFileItemFactory factory = new DiskFileItemFactory();
        //通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区时,将他放到临时文件中
        if(dir!=null){
            factory.setSizeThreshold(1024 * 1024);//缓存区大小为1M
            factory.setRepository(dir);
        }
        return factory;
    }
}
DownloadServlet
package com.example.file;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;

@WebServlet(name = "UploadServlet", value = "/UploadServlet")
public class UploadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {


    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {


        //判断上传的文件时普通表单还是带文件的表单
        if (!ServletFileUpload.isMultipartContent(request)) {
            return;//终止方法运行,说明这是个普通表单,直接返回
        }
        //创建上传文件的保存路径 建议保存到WEB-INF路径下 安全 用户无法直接访问上传的文件
        String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
        File uploadFile = new File(uploadPath);
        if (!uploadFile.exists()) {
            uploadFile.mkdir();//创建这个目录
        }
        //缓存,临时文件
        String tmpPath = this.getServletContext().getRealPath("/WEB-INF/tmp");
        File tmpFile = new File(tmpPath);
        if (!tmpFile.exists()) {
            tmpFile.mkdir();//创建这个临时目录
        }
        //处理上传的文件
        /*
        ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象
        在使用ServletFileUpload对象解析请求时需要DiskFileItemFactory对象
        所以 Wimbledon需要在进行解析工作前构造好DiskFileItemFactory对象
        通过ServletFileUpload对象的构造方法或setFileItemFactory()方法设置ServletFileUpload对象的fileItemFactory属性
         */

        DiskFileItemFactory factory = getDiskFileItemFactory(tmpFile);
        ServletFileUpload servletFileUpload = getServletFileUpload(factory);
        String result = extracted(request, uploadPath, servletFileUpload);
        request.setAttribute("result",result);
        System.out.println(result);
        request.getRequestDispatcher("info.jsp").forward(request,response);



    }

    private String extracted(HttpServletRequest request, String uploadPath, ServletFileUpload servletFileUpload){
        StringBuilder msg = new StringBuilder();
        try{
            //3.处理上传的文件
            //吧前端请求解析 封装成一个FileItem对象
            List<FileItem> fileItems = servletFileUpload.parseRequest(request);
            for (FileItem fileItem : fileItems) {
                String name;
                if(fileItem.isFormField()){
                    //getFileName指的是前端表单控件Name
                    name = fileItem.getFieldName();
                    String value = fileItem.getString("UTF-8");//处理乱码
                    System.out.println(name+":"+value);
                }else {//文件
                    //----------------处理文件-----------------//
                    //获取文件全名
                    name = fileItem.getName();
                    //判断文件大小
                    if(fileItem.getSize() >= 1024 * 1024 * 10){
                        msg.append(name).append(":").append("超过内存限制,拒绝传输<br>");
                        fileItem.delete();//上传成功,清除临时文件
                        continue;
                    }
                    //可能存在文件名不合法的情况
                    if(name == null|| name.trim().equals("")){
                        fileItem.delete();//上传成功,清除临时文件
                        continue;
                    }
                    //获取上传的文件名
                    String fileName = name.substring(name.lastIndexOf('/') + 1);
                    //获取文件后缀
                    String fileExtName = name.substring(name.lastIndexOf('.') + 1);
                /*
                     如果后缀名不是需要的则直接return不处理
                     告诉用户文件类型不正确
                 */

                    //可以使用UUID(唯一识别的通识码)保证文件名唯一
                    //UUID.randomUUID(),随机产生一个识别的通用码

                    //网络传输中的东西都需要序列化
                    //实体类,如果想要在多个计算机上运行,需要传输(把对象都序列化)
                    //implement Serializable : 标记接口,JVM---> Java栈  本地方法栈 native -->C++
                    String uuidPath = UUID.randomUUID().toString();


                    //----------------存放地址-----------------//
                    //存放位置 uploadPath
                    //文件真实存在的路径 realPath = uploadPath/uuid_fileName
                    String realPath = uploadPath + "/" +uuidPath + "_" + name;
                    System.out.println(realPath);


                    //----------------文件传输-----------------//
                    //获得文件上传的流
                    InputStream is = fileItem.getInputStream();

                    //创建一个文件输出流
                    FileOutputStream fos = new FileOutputStream(realPath);

                    //创建一个缓冲区
                    byte[] buffer = new byte[1024 * 1024];

                    //判断是否读取完毕
                    int len =0;
                    //如果大于0说明还存在数据
                    while ((len=is.read(buffer))>0){
                        fos.write(buffer,0,len);
                    }

                    //关闭流
                    fos.close();
                    is.close();

                    msg.append(name).append(":上传成功<br>");

                    fileItem.delete();//上传成功,清除临时文件
                }
            }

        }catch (Exception e){
            e.printStackTrace();
            return "上传异常";
        }

        return msg.toString();
    }

    private ServletFileUpload getServletFileUpload(DiskFileItemFactory factory) {
        //2.获取ServletFileUpload
        ServletFileUpload servletFileUpload = new ServletFileUpload(factory);
        //监听文件上传进度
//        servletFileUpload.setProgressListener((l, all, i) -> System.out.println("总大小:" + all + "\t已上传:" + l));
        //处理乱码问题
        servletFileUpload.setHeaderEncoding("UTF-8");
        //设置单个文件的最大值
//        servletFileUpload.setFileSizeMax(1024 * 1024 * 10);//10M
        //设置总共能够上传文件的大小
//        servletFileUpload.setSizeMax(1024 * 1024 * 10 * 2);
        /*
            如果设置了大小则如果出现超过大小的情况 则直接异常
         */
        return servletFileUpload;
    }

    private DiskFileItemFactory getDiskFileItemFactory(File dir) {
        //1.创建DiskFileItemFactory对象,处理文件上传路径或者大小限制
        DiskFileItemFactory factory = new DiskFileItemFactory();
        //通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区时,将他放到临时文件中
        if(dir!=null){
            factory.setSizeThreshold(1024 * 1024);//缓存区大小为1M
            factory.setRepository(dir);
        }
        return factory;
    }
}

迷糊与疑问

  1. 当我给ServletFileUpload设置了大小限制时,如果前端传入的文件超过了限制会出现异常,不是普通的异常,它会导致request直接坏掉,后续进行转发与重定向时都不能得到想要的效果,参考网上博客(捕捉异常 在catch块中转发、重定向)依然无法解决问题。

参考与致谢

oad servletFileUpload = new ServletFileUpload(factory);
//监听文件上传进度
// servletFileUpload.setProgressListener((l, all, i) -> System.out.println(“总大小:” + all + “\t已上传:” + l));
//处理乱码问题
servletFileUpload.setHeaderEncoding(“UTF-8”);
//设置单个文件的最大值
// servletFileUpload.setFileSizeMax(1024 * 1024 * 10);//10M
//设置总共能够上传文件的大小
// servletFileUpload.setSizeMax(1024 * 1024 * 10 * 2);
/*
如果设置了大小则如果出现超过大小的情况 则直接异常
*/
return servletFileUpload;
}

private DiskFileItemFactory getDiskFileItemFactory(File dir) {
    //1.创建DiskFileItemFactory对象,处理文件上传路径或者大小限制
    DiskFileItemFactory factory = new DiskFileItemFactory();
    //通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区时,将他放到临时文件中
    if(dir!=null){
        factory.setSizeThreshold(1024 * 1024);//缓存区大小为1M
        factory.setRepository(dir);
    }
    return factory;
}

}


### 迷糊与疑问

1. 当我给ServletFileUpload设置了大小限制时,如果前端传入的文件超过了限制会出现异常,不是普通的异常,它会导致request直接**坏掉**,后续进行转发与重定向时都不能得到想要的效果,参考网上博客(捕捉异常 在catch块中转发、重定向)依然无法解决问题。

### 参考与致谢

1. 感谢 B站狂神说 提供的javaweb学习视频,本文参考该视频编写
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值