Servlet实现文件上传和下载

  • 文件上传
    1. 还是使用表单提交,action还是按照以前规定来指定,method指定为post
    2. enctype:encodetype编码类型默认是application/X-www-form-urlencoded即url编码这种编码不适二进制文件数据的提交,一股是适用文本
    3. 如果是要进行二进制文件的提交enctype要指定multipart/form-data表示表单提交的数据是有多个部分组成,也就是可以提交二进制数据和文本数据
  • 文件上传的代码演示
  • 前端部分
  • 需要指定表单的类型为:"enctype=multipart/form-data",请求方式为post
<%--
  Created by IntelliJ IDEA.
  User: 韩顺平
  Version: 1.0
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- 指定了base标签 -->
    <base href="<%=request.getContextPath()+"/"%>>">
    <style type="text/css">
        input[type="submit"] {
            outline: none;
            border-radius: 5px;
            cursor: pointer;
            background-color: #31B0D5;
            border: none;
            width: 70px;
            height: 35px;
            font-size: 20px;
        }

        img {
            border-radius: 50%;
        }

        form {
            position: relative;
            width: 200px;
            height: 200px;
        }

        input[type="file"] {
            position: absolute;
            left: 0;
            top: 0;
            height: 200px;
            opacity: 0;
            cursor: pointer;
        }
    </style>

    <script type="text/javascript">
        function prev(event) {
            //获取展示图片的区域
            var img = document.getElementById("prevView");
            //获取文件对象
            var file = event.files[0];
            //获取文件阅读器: Js的一个类,直接使用即可
            var reader = new FileReader();
            reader.readAsDataURL(file);
            reader.onload = function () {
                //给img的src设置图片url
                img.setAttribute("src", this.result);
            }
        }
    </script>

</head>
<body>
<!-- 表单的enctype属性要设置为multipart/form-data
    enctype="multipart/form-data" 表示提交的数据是多个部分构造,有文件和文本
 -->

<form action="fileUpLoad" method="post" enctype="multipart/form-data">
    家居图: <img src="2.jpg" alt="" width="200" height="200" id="prevView">
<%--    小伙伴愿意完成自己测试--%>
    <input type="file" name="pic" id="" value="" onchange="prev(this)"/>

    家居名: <input type="text" name="name"><br/>

    <input type="submit" value="上传"/>
</form>
</body>
</html>

  • 后端部分
  • 通过ServletFileUpload类的isMultipartContent()方法进行判断是不是文件表单,通过判断进行分类处理
  • 创建一个DiskFileItemFactory对象,用于构建解析表单的工具对象(ServletFileUpload)
  • 处理乱码问题,将ServletFileUpload表单解析工具对象的编码设置为utf-8
  • 通过ServletFileUpload对象的parseRequest()方法,将表单提交的每一个表单项都封装成一个FileItem对象,并返回一个存放所有表单项FileItem对象的List集合
  • 使用FileItem对象的isFormField()方法判断表单项是文本,还是文件
  • 通过FileItem对象的write()方法可以将文件拷贝到指定的目录下
  • 防止文件名重复可以使用,"UUID+时间戳+源文件名"的方式命名文件
package com.hsp.servlet; /**
 * @Projectname: fileupdown
 * @Filename: ${NAME}
 * @Author: 杨逸
 * @Data:2023/4/16 16:18
 * @Description: TODO
 */

import com.hsp.utils.WebUtils;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

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

public class FileUpLoadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

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

        // 1.判断表单是否是文件表单
        //使用ServletFileUpload类的isMultipartContent()方法进行判断
        if (ServletFileUpload.isMultipartContent(request)) {
            //2.创建一个DiskFileItemFactory对象,用于构建解析表单的工具对象
            DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
            //3.创建一个ServletFileUpload工具对象,需要传入一个DiskFileItemFactory对象
            ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
            //处理上传文件名是中文是的乱码问题
            //设置表单解析工具的编码
            servletFileUpload.setHeaderEncoding("utf-8");
            try {
                //4.使用ServletFileUpload工具对象的parseRequest()方法,
                // 将表单提交的每一个表单项都封装成一个FileItem对象,
                // 并返回一个存放所有表单项FileItem对象的List集合
                List<FileItem> fileItemList = servletFileUpload.parseRequest(request);
                //遍历对每一个表单项进行处理
                for (FileItem fileItem : fileItemList) {
                    System.out.println(fileItem);
                    //判断该表单项是不是文本类型的,
                    if (fileItem.isFormField()) {//返回true,是文本
                        //获得文本的字符串
                        String string = fileItem.getString("utf-8");
                        System.out.println("文本内容" + string);
                    } else {//返回false,是文件类型的
                        //获得文件名
                        String name = fileItem.getName();
                        System.out.println("文件名=" + name);
                        //将上传的文件保存到指定的路径
                        String path = "/upload";
                        //获得项目的全路径
                        String contextPath = getServletContext().getRealPath(path);
                        //根据当前时间创建保存上传文件的目录
                        String date = WebUtils.getDate();
                        contextPath = contextPath +date;
                        //创建保存文件的目录
                        File file = new File(contextPath);
                        //判断目录是否已经存在
                        if (!file.exists()){
                            //如果不存在就创建
                            file.mkdirs();
                        }
                        //文件拷贝前,对文件名进行处理,解决文件名重复的问题
                        //通过"UUID+时间戳+源文件名"的拼接,防止因文件名重复导致文件被覆盖的问题
                        UUID uuid = UUID.randomUUID();
                        name = uuid + "_" + System.currentTimeMillis() + "_" +name;
                        //将上传的文件拷贝到指定的位置
                        String fileFullPath = contextPath + "/" +name;
                        fileItem.write(new File(fileFullPath));

//                        返回响应信息
                        response.setContentType("text/html;charset=utf-8");
                        response.getWriter().write("上传成功!");
                    }
                }

            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        } else {

        }
    }
}

  • 文件下载
    • 文件下载需要设置http的响应头,Content-Disposition:表示下载的数据的展示方式,比如是内联形式(网页形式或者网页一部分)或者是文件下载方式attachment,默认是网页的形式
    • 总体思路:使用输入流读取要下载的资源,然后使用http响应的输出流返回给客户端
  • 文件下载代码演示
  • 前端部分
<%--
  Created by IntelliJ IDEA.
  User: 杨逸
  Date: 2023/4/16     
  Time: 17:45
  Version: 1.0
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>文件下载</title>
    <base href="<%=getServletConfig().getServletContext().getContextPath()%>/ "/>
</head>
<body>
<h1>文件下载</h1>
<a href="fileDownload?name=1.png">下载图片</a><br><br>
<a href="fileDownload?name=韩顺平Java工程师课程-JavaWeb.pdf">下载JavaWeb基础笔记</a><br>
</body>
</html>

  • 后端部分
  • 下载文件需要给http响应设置context-type的MIME类型,
  • 给http响应设置Content-Disposition,指定响应的显示形式
  • 这里要考虑不同浏览器的编码问题,chrome浏览器是URL编码,firefox浏览器是BASE64
package com.hsp.servlet; /**
 * @Projectname: fileupdown
 * @Filename: ${NAME}
 * @Author: 杨逸
 * @Data:2023/4/16 17:47
 * @Description: TODO
 */

import org.apache.commons.io.IOUtils;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.Base64;

import static javax.xml.crypto.dsig.Transform.BASE64;

@WebServlet(name = "FileDownloadServlet", value = "/FileDownloadServlet")
public class FileDownloadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //在下载之前需要准备好要被下载的文件,保证out目录下有该资源
        //设置编码
        request.setCharacterEncoding("utf-8");
        //获得请求下载的文件名
        String name = request.getParameter("name");
        //通过servletContext获得要下载文件的MIME类型
        ServletContext servletContext = getServletContext();
        String download = "/download";//保存下载资源的路径
        String mimeType = servletContext.getMimeType(download + name);
        //给http响应设置context-type的MIME类型,
        response.setContentType(mimeType);

        //给http响应设置Content-Disposition,指定响应的显示形式
        //这里要考虑不同浏览器的编码问题
        //firefox与其他的浏览器不太一样,需要单独设置
        if (request.getHeader("User-Agent").contains("firefox")){
            //如果是firefox浏览器,使用的是base64编码
            Base64.Encoder encoder = Base64.getEncoder();
            response.setHeader("Content-Dispositon","attachment;filename=?UTF-8?B" + encoder.encode(name.getBytes("utf-8"))+"?=");
        }else {
            //如果是其他浏览器,如chrome,ie,使用的是url编码
            response.setHeader("Content-Dispositon","attachment;filename=" + URLEncoder.encode(name,"utf-8"));
        }
        //读取要下载的文件,准备返回给客户端
        InputStream resourceAsStream = servletContext.getResourceAsStream(download +"/"+ name);
        //获得用于返回数据的输出流,用于返回二进制数据
        ServletOutputStream outputStream = response.getOutputStream();
        //使用工具类,将输入流关联的文件,拷贝到输出流,并返回给客户端
        IOUtils.copy(resourceAsStream,outputStream);//这里拷贝和返回一起完成

    }

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