目录

1. 文章说明

2. JavaWeb实现文件上传下载

2.1 JavaWeb实现文件上传的步骤

2.2 JavaWeb实现文件上传代码

2.3 文件上传的HTTP协议说明

2.4 JavaWeb实现文件下载

 3. SpringMVC实现文件上传下载

3.1 SpringMVC实现文件上传的步骤


1. 文章说明

文件上传在Web项目开发中是一个很重要的功能点,随着技术(框架)的更新变化,不同的技术对文件上传有不同的实现方式。在上传和下载文件的使用操作上,新的技术比旧的技术实现了更便捷(更容易上手)操作方式,这是因为新技术对旧技术进行了更深层次的封装,屏蔽了底层的一些实现细节,但是这也导致我们在使用新技术的时候,大多数时候只是停留在会使用的层面,而不明白这些技术的演变过程和底层实现原理,本篇文章就从传统的JavaWeb 到 SpringMVC 再到SpringBoot实现文件上传的方式做一个总结。

2. JavaWeb实现文件上传下载

2.1 JavaWeb实现文件上传的步骤

  1. 引入可以实现文件上传的两个jar包 commons-fileupload-1.2.1.jar 和 commons-io-1.4.jar
  2. 写一个如下要求的form表单
  • 表单提交方式必须为post
  • form标签的enctype属性值必须为multipart/form-data 【enctype=multipart/form-data表示提交的数据以多段(每一个表单项一个数据段)的形式进行拼接,然后以二进制流的形式发送给服务器,因为客户端是以流的形式把信息发送给服务器段,所以在服务器段接收的时候,也需要以流的形式进行接收】
  • form表单中使用 input="file"添加要上传的文件
  • 编写服务器代码(Servlet程序)接收并处理上传的数据

2.2 JavaWeb实现文件上传代码

  1. file_upload.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>文件上传</title>
</head>
<body>
    <form action="http://localhost:8080/book/uploadServlet" 
          enctype="multipart/form-data" method="post">
        用户名:<input type="text" name="username"><br/>
        头  像:<input type="file" name="photo"><br/>
              <input type="submit" value="提交">
    </form>
</body>
</body>
</html>

2. UploadServlet.java

package com.example.web;

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

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.List;

public class UploadServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1. 判断上传的数据是否为多段数据
        if(ServletFileUpload.isMultipartContent(req)){
            // 2. 创建FileItemFactory工厂实现类
            FileItemFactory fileItemFactory = new DiskFileItemFactory();
            // 3. 创建用于解析上传数据的工具类ServletFileUpload类
            ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
            try {
                // 4. 解析上传的数据,得到每一个表单项FileItem
                List<FileItem> fileItems = servletFileUpload.parseRequest(req);
                // 5. 循环判断每一个表单项,是普通表单项还是文件表单项
                for (FileItem fileItem : fileItems) {
                    if(fileItem.isFormField()){
                        // 普通表单项
                        System.out.println("表单项的name值:"+fileItem.getFieldName());
                        System.out.println("表单项的value值:"+fileItem.getString("UTF-8"));
                    }else {
                        // 文件表单项
                        System.out.println("表单项的name值:"+fileItem.getFieldName());
                        System.out.println("上传的文件名:"+fileItem.getName());
                        // 保存文件到服务器指定路径下
                        fileItem.write(new File("D:\\"+fileItem.getName()));
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

2.3 文件上传的HTTP协议说明

        说明:图中 “上传的文件的数据” 并没有显示出要上传的文件数据,是因为浏览器检测到是二进制流的文件数据,就不再展示在此处了(Chrome浏览器不展示,FireFox浏览器会展示上传的文件的二进制流数据)

Java Excel 上传下载原理 javaweb上传下载_文件上传

2.4 JavaWeb实现文件下载

1. DownloadFileServlet

package com.example.web;

import org.apache.commons.io.IOUtils;
import sun.misc.BASE64Encoder;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;

public class DownloadFileServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1、获取要下载的文件名(此处为了演示方便,写死在代码中)
        String downloadFileName = "3.jpg";

        //2、读取要下载的文件内容 (通过ServletContext对象可以读取)
        ServletContext servletContext = getServletContext();

        //3、获取要下载的文件类型
        String mimeType = servletContext.getMimeType("/pages/file/" + downloadFileName);
        System.out.println("下载的文件类型:" + mimeType);

        //4、在回传前,通过响应头告诉客户端返回的数据类型
        resp.setContentType(mimeType);

        //5、还要告诉客户端收到的数据是用于下载使用(还是使用响应头来告诉客户端)
        // Content-Disposition响应头,表示收到的数据怎么处理
        // attachment表示附件,表示下载使用
        // filename= 表示指定下载的文件名
        // url编码是把汉字转换成为%xx%xx的格式
        if (req.getHeader("User-Agent").contains("Firefox")) {
            // 如果是火狐浏览器使用Base64编码
            resp.setHeader("Content-Disposition", "attachment; filename==?UTF-8?B?" + new BASE64Encoder().encode("中国.jpg".getBytes("UTF-8")) + "?=");
        } else {
            // 如果不是火狐,是IE或谷歌,使用URL编码操作
            resp.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("中国.jpg", "UTF-8"));
        }
        /**
         * /斜杠被服务器解析表示地址为http://ip:prot/工程名/  映射 到代码的Web目录
         */
        InputStream resourceAsStream = servletContext.getResourceAsStream("/pages/file/" + downloadFileName);
        // 获取响应的输出流
        OutputStream outputStream = resp.getOutputStream();
        //3、把下载的文件内容回传给客户端 ,读取输入流中全部的数据,复制给输出流,输出给客户端
        IOUtils.copy(resourceAsStream, outputStream);
    }
}

2. 文件下载的HTTP协议头

  • 火狐浏览器

Java Excel 上传下载原理 javaweb上传下载_文件上传_02

  •  谷歌浏览器

Java Excel 上传下载原理 javaweb上传下载_上传_03

 3. SpringMVC实现文件上传下载

3.1 SpringMVC实现文件上传的步骤

  • 在pom.xml文件中导入文件上传所需的jar包坐标
<!--文件上传-->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.4</version>
        </dependency>
  • 要求form表单提交方式为post,且添加属性enctype=multipartr/form-data
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>测试文件上传和下载</title>
</head>
<body>
<a th:href="@{/testDown}">通过ResponseEntity下载服务器中的文件到客户端</a><br/>
<form th:action="@{/testUp}" method="post" enctype="multipart/form-data">
    用户名:<input type="text" name="username"><br/>
    头  像:<input type="file" name="photo"><br>
           <input type="submit" value="上传">
</form>
</body>
</html>
  • SpringMVC中将上传的文件封装到MultipartFile对象中,通过此对象可以获取文件相关信息,需要在SpringMVC的配置文件中添加如下配置:
<!--必须通过文件解析器的解析才能将文件转换为MultipartFile对象-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons."></bean>
  • SpringMVC中的控制器方法
// 文件上传
    @RequestMapping("/testUp")
    public String testUp(MultipartFile photo,HttpSession session,String username) throws IOException {
        // 可以通过控制器方法的形参获取普通表单项的内容
        System.out.println("username = " + username);

        //获取上传的文件的文件名
        String fileName = photo.getOriginalFilename();
        System.out.println("fileName = " + fileName);

        //处理文件重名问题
        String hzName = fileName.substring(fileName.lastIndexOf("."));
        System.out.println("hzName = " + hzName);
        fileName = UUID.randomUUID() + hzName;
        System.out.println("hzName = " + hzName);

        //获取服务器中photo目录的路径
        ServletContext servletContext = session.getServletContext();
        String photoPath = servletContext.getRealPath("photo");
        System.out.println("photoPath = " + photoPath);
        File file = new File(photoPath);
        if(!file.exists()){
            file.mkdir();
        }
        String finalPath = photoPath + File.separator + fileName;
        System.out.println("finalPath = " + finalPath);
        //实现上传功能,需要使用到 commons-fileupload-1.2.1.jar 及其所依赖的 commons-io-1.4.jar
        photo.transferTo(new File(finalPath));
        return "success";
    }
// 文件下载
    @RequestMapping("/testDown")
    public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
        //获取ServletContext对象
        ServletContext servletContext = session.getServletContext();
        //获取服务器中文件的真实路径
        String realPath = servletContext.getRealPath("/static/img/1.jpg");
        System.out.println("realPath = " + realPath);
        //创建输入流
        InputStream is = new FileInputStream(realPath);
        //创建字节数组
        //byte[] bytes = new byte[is.available()];
        byte[] buffer = new byte[1024];
        int len = 0;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        while ((len = is.read(buffer)) != -1){
            out.write(buffer,0,len);
        }
        byte[] bytes = out.toByteArray();
        //将流读到字节数组中
        //is.read(bytes);
        //创建HttpHeaders对象设置响应头信息
        MultiValueMap<String, String> headers = new HttpHeaders();
        //设置要下载方式以及下载文件的名字
        headers.add("Content-Disposition", "attachment;filename=1.jpg");
        String mimeType = servletContext.getMimeType("/static/img/1.jpg");
        headers.add("Content-Type",mimeType);
        //设置响应状态码
        HttpStatus statusCode = HttpStatus.OK;
        //创建ResponseEntity对象
        ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(bytes, headers, statusCode);
        //关闭输入流
        is.close();
        return responseEntity;
    }