12、JavaWeb启程——Servlet3.0文件上传与下载

发布于:2022-11-06 ⋅ 阅读:(314) ⋅ 点赞:(0)

1、项目准备

  • 新建一个web项目,添加对应的jar包

2、编写register.jsp

  • method:需要使用post请求,get请求限制了数据的大小
  • enctype使用multipart/form-data,不然直接报错(需要二进制数据)。
  • 提供文件上传的控件。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
	<title>注册</title>
</head>
<body>
	<form action="/fileUpload"method="post"enctype="multipart/form-data">
		<p>账号:<input type="text" name="username"/></p>
		<p>头像:<input type="file" name="headImg"/></p>
		<input type="submit" value="注册">
	</form>
</body>
</html>

【结果预览】:
在这里插入图片描述

3、Servlet3.0文件上传

1、API

返回值 方法 作用
Part getPart(String name) 用于获取请求中指定name文件
Collection getParts() 获取请求中的全部文件

2、Part中的常用方法

返回值 方法 作用
void write(String fileName) 直接把接收到的文件保存到磁盘中
void getContentType() 获取文件的类型 MIME
String getHeader(String name) 获取请求头信息
long getSize() 获取文件的大小

3、基本使用代码示例

package cn.simplelife.work._01fileupload;

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.Collection;
import java.util.Scanner;
import java.util.UUID;

/**
 * @ClassName FileUploadServlet
 * @Description
 * @Author simplelife
 * @Date 2022/10/28 10:53
 * @Version 1.0
 */
@MultipartConfig(maxFileSize = 1024 * 500, maxRequestSize = 1024 * 500)
@WebServlet(value = "/register")
public class FileUploadServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1、上传文件的基本使用
        req.setCharacterEncoding("utf-8");
        String username = req.getParameter("username");
        // 获取文件
        Part headImg = req.getPart("headImg");
        // 获取文件提交时的名称
        String submittedFileName = headImg.getSubmittedFileName();
        // 将文件写入到指定的目录下的文件中
        headImg.write("F:\\JavaWeb\\javaweb\\day18\\web\\upload\\" + submittedFileName);
    }
}

4、获取上传的文件名称

返回值 方法 作用
String getHeader(“contentdisposition”) Tocmat 8.0 之前使用通过请求头获取文件名,需截取字符串
String getSubmittedFileName() Tomcat8.0 之后提供的直接获取文件名方式

5、文件名相同覆盖现有文件名称解决

【问题概述】: 若上传得文件名相同会导致覆盖服务器之前已上传的的文件。

【解决方案】: 使用唯一的文件名称作为上传后的文件名称。我们使用UUID来做。

【代码演示】:

package cn.simplelife.work._01fileupload;

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.Collection;
import java.util.Scanner;
import java.util.UUID;

/**
 * @ClassName FileUploadServlet
 * @Description
 * @Author simplelife
 * @Date 2022/10/28 10:53
 * @Version 1.0
 */
@MultipartConfig(maxFileSize = 1024 * 500, maxRequestSize = 1024 * 500)
@WebServlet(value = "/register")
public class FileUploadServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 2、生成唯一的文件名称----->防止用户上传相同的文件名称覆盖掉之前的文件信息
        req.setCharacterEncoding("utf-8");
        String username = req.getParameter("username");
        // 获取文件
        Part headImg = req.getPart("headImg");
        // 获取提交时的文件名称
        String submittedFileName = headImg.getSubmittedFileName();
        // 生成唯一的文件名称
        String filenamePre = UUID.randomUUID().toString();
        // 获取文件的拓展名称
        String substring = submittedFileName.substring(submittedFileName.lastIndexOf("."));
        // 保存文件名称
        String realName = filenamePre + substring;
        // 保存文件到磁盘
        headImg.write("F:\\JavaWeb\\javaweb\\day18\\web\\upload\\" + realName);
	}
}

6、文件保存位置问题解决

【问题概述】: 文件在磁盘某个位置,不在项目下,无法使用 HTTP 协议访问,所以要把用户上传的文件存放到项目中才可通过 HTTP 协议来访问,且保存的位置路径不可以写绝对路径。

【解决方法】: 可以通过ServletContext 对象的 getRealPath(“项目中保存上传文件的文件夹的相对路径”) 来获取其的绝对路径。

【代码演示】:

package cn.simplelife.work._01fileupload;

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.Collection;
import java.util.Scanner;
import java.util.UUID;

/**
 * @ClassName FileUploadServlet
 * @Description
 * @Author simplelife
 * @Date 2022/10/28 10:53
 * @Version 1.0
 */
@MultipartConfig(maxFileSize = 1024 * 500, maxRequestSize = 1024 * 500)
@WebServlet(value = "/register")
public class FileUploadServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		 // 3、文件保存位置的问题
        req.setCharacterEncoding("utf-8");
        String username = req.getParameter("username");
        // 获取文件
        Part headImg = req.getPart("headImg");
        // 获取文件提交时的文件名称
        String submittedFileName = headImg.getSubmittedFileName();
        // 生成唯一的文件名称
        String realName = UUID.randomUUID().toString() + submittedFileName.substring(submittedFileName.lastIndexOf("."));
        // 自动获取项目路径的根路径,然后拼接上给的路径
        String baseDir = req.getServletContext().getRealPath("/upload/");
        // 将上传的文件写入文件
        headImg.write(baseDir + realName);
	}
}

7、文件上传类型的限制

【问题概述】: 限制用户恶意上传文件,比如要让用户上传头像,而用户却上传一个非图片文件,比如 JSP 文件。

【解决方案】: 判断用户上传的文件的类型是否符合需要的文件类型。

【代码演示】:

package cn.simplelife.work._01fileupload;

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.Collection;
import java.util.Scanner;
import java.util.UUID;

/**
 * @ClassName FileUploadServlet
 * @Description
 * @Author simplelife
 * @Date 2022/10/28 10:53
 * @Version 1.0
 */
@MultipartConfig(maxFileSize = 1024 * 500, maxRequestSize = 1024 * 500)
@WebServlet(value = "/register")
public class FileUploadServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 4、文件上传类型约束
        req.setCharacterEncoding("utf-8");
        String username = req.getParameter("username");
        // 获取文件
        Part headImg = req.getPart("headImg");
        // 判断文件的类型是否为图片类型,不是图片类型不上传
        if (headImg.getContentType().startsWith("image/")) {
        // 获取文件上传时的名称
        String submittedFileName = headImg.getSubmittedFileName();
        // 生成唯一的文件名称
        String realName = UUID.randomUUID().toString() + submittedFileName.substring(submittedFileName.lastIndexOf("."));
        // 自动获取文件的根路径
        String baseDir = getServletContext().getRealPath("/upload/");
        // 将上传的文件写入文件
        headImg.write(baseDir + realName);
        }
	}
}

8、文件大小的约束

【问题说明】: 文件上传限制大小可提高服务器硬盘的使用率,防止用户恶意上传文件造成服务器磁盘资源紧张。

**【解决方案】:**可以通过设置 @MutipartConfig 的属性做限制

  • maxFileSize:单个上传文件大小限制,单位:bytes
  • maxRequestSize:显示请求中数据的大小,单位:bytes

【代码演示】:

package cn.simplelife.work._01fileupload;

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.Collection;
import java.util.Scanner;
import java.util.UUID;

/**
 * @ClassName FileUploadServlet
 * @Description
 * @Author simplelife
 * @Date 2022/10/28 10:53
 * @Version 1.0
 */
@MultipartConfig(maxFileSize = 1024 * 500, maxRequestSize = 1024 * 500)
@WebServlet(value = "/register")
public class FileUploadServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 5、文件上传大小的限制
        req.setCharacterEncoding("utf-8");
        String username = req.getParameter("username");
        try {
            // 获取上传的文件 ---多个文件
            Collection<Part> parts = req.getParts();
            // 遍历多个文件
            for (Part part : parts) {
                // 获取每一个文件的文件名称
                String contentType = part.getContentType();
                if (contentType != null && contentType.startsWith("image/")) {
                    // 获取每一个文件上传的文件名称
                    String submittedFileName = part.getSubmittedFileName();
                    // 生成唯一的文件名称
                    String realName = UUID.randomUUID().toString() + submittedFileName.substring(submittedFileName.lastIndexOf("."));
                    // 获取保存的根路径
                    String baseDir = req.getServletContext().getRealPath("/upload/");
                    // 写入文件
                    part.write(baseDir + realName);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            req.setAttribute("error", "亲,文件太大了!");
            req.getRequestDispatcher("/register.jsp").forward(req, resp);
        }
    }
}

4、Servlet3.0文件下载

1、文件下载的简单实现

  • 在web目录下提供下载的资源
  • 编写下载页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/download/cat.png"></a>
<a href="${pageContext.request.contextPath}/download/cat.rar"></a>
</body>
</html>

2、文件下载的限制

【问题概述】: 下载功能已经实现,但是文件放在 WEB-INF 外面不安全,用户只需要拿到下载的超链接都能够下载,实际开发中,我们的文件通常需要用户有一定的权限才能下载。

【解决方法】:

  • 将需要下载的文件存放到WEB-INF目录下,通过Servlet进行下载操作。

【代码演示】:

  • 移动下载目录文件到WEB-INF目录下
  • 修改下载页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/download?fileName=cat.png"></a>
<a href="${pageContext.request.contextPath}/download?fileName=cat.rar"></a>
</body>
</html>
  • 编写servlet
package cn.simplelife.work._02filedownload;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;

/**
 * @ClassName FileDownLoad
 * @Description
 * @Author simplelife
 * @Date 2022/10/28 16:09
 * @Version 1.0
 */
@WebServlet(value = "/download")
public class FileDownLoad extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //接收用户要下载的文件名称
        req.setCharacterEncoding("utf-8");
        String fileName = req.getParameter("fileName");
        //在响应之前做其他操作
        System.out.println("扣钱");
        //在项目下找到文件并响应给用户
        String realPath = req.getServletContext().getRealPath("/WEB-INF/download/");
        // 使用工具类 Files 的 copy 方法获取文件输入流,响应回浏览器
		Files.copy(Paths.get(realPath, fileName), resp.getOutputStream());
    }
}

3、下载文件名称问题

【问题概述】: Tomcat 服务器未告知浏览器文件的名称,所以需要手动设置响应头来告知浏览器文件名称。

【代码演示】:

package cn.simplelife.work._02filedownload;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;

/**
 * @ClassName FileDownLoad
 * @Description
 * @Author simplelife
 * @Date 2022/10/28 16:09
 * @Version 1.0
 */
@WebServlet(value = "/download")
public class FileDownLoad extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //接收用户要下载的文件名称
        req.setCharacterEncoding("utf-8");
        String fileName = req.getParameter("fileName");
        //在响应之前做其他操作
        System.out.println("扣钱");
        //在项目下找到文件并响应给用户
        String realPath = req.getServletContext().getRealPath("/WEB-INF/download/");
        // 获取浏览器类型
        String header = req.getHeader("User-Agent");
        // 根据浏览器类型设置文件下载的名称
        String name = header.contains("MSIE") ? URLEncoder.encode(fileName, StandardCharsets.UTF_8) : new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
        resp.setHeader("Content-Disposition", "attachment;filename=" + name);
        //复制文件
        //Paths.get(realPath, fileName) 获取到文件的真实路径
        //Files.copy(Path,输出流)
        Files.copy(Paths.get(realPath, fileName), resp.getOutputStream());
    }
}