Java 基础

JavaWeb 文件上传与邮件发送

2022-03-14  本文已影响0人  yjtuuige

一、JavaWeb 文件上传

1.1 概述

1.2 使用到的类

  1. ServletFileUpload
  1. FileItem
方法名 描述
boolean isFormField() 判断是否是文本表单,是返回 true,否则返回 false
string getFieldName() 返回表单标签 name 的属性值
string getstring() 数据流内容,以字符串返回
string getName() 获得上传文件名
Inputstream getInputstream() 以流的形式,返回上传文件的数据内容
void delete() 删除文件
  1. DiskFileItemFactory

1.3 代码的实现逻辑

  1. 判断用户上传的文件,是普通表单,还是带文件的表单,如果是普通文件,直接返回(return);
  2. 创建文件上传保存的路径,WEB-INF 路径下是安全的,用户无法直接访问;
  3. 创建临时文件路径,如果文件超过预期大小,放到一个临时文件中;
  4. 创建 DiskFileItemFactory 对象,设置文件临时路径 setRepository,或大小的限制(设置缓冲区,setSizeThreshold);
  5. 获取 ServletFileUpload 对象,处理乱码问题 setHeaderEncoding ,设置单个文件的最大值 setFileSizeMax ,设置总共能够上传文件的大小 setSizeMax ,还可选择监听文件上传进度 setProgressListener
  6. 通过 ServletFileUpload 对象的 parseRequest 方法,把前端请求解析,封装成一个FileItem 对象;
  7. 判断上传的文件,是普通的表单,还是带文件的表单 isFormFieldgetFieldName 获取前端表单控件的 name
  8. 通过 FileItemgetName 方法,获取文件名,并判断是否为空,可以使用 UUID 保证文件名唯一性;
  9. 拼接文件真实存在路径,并给每个文件,创建一个对应的文件夹;
  10. 通过 FileItemgetInputStream 方法获取文件上传的流,并创建一个文件输出流,将文件写入真实保存路径,最后关闭流;
  11. 上传成功,将页面转发到新的提示页面。

1.4 代码实现

<form action="${pageContext.request.contextPath}/upload.do" enctype="multipart/form-data" method="post">
    <p>上传用户:<input type="text" name="username"></p>
    <p>上传文件:<input type="file" name="file1"></p>
    <p>上传文件:<input type="file" name="file2"></p>
    <p>
        <input type="submit" name="submit"> | <input type="reset" name="reset">
    </p>
</form>
package com.study.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.RequestContext;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

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("/upload.do")
public class UploadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 判断上传文件是普通表单,还是带文件的表单
        if (!ServletFileUpload.isMultipartContent(new Rc(req))) {
            // 普通表单,终止方法运行,直接返回
            return;
        }
        // 创建文件保存路径,建议WEB-INF目录下,安全,用户无法直接访问
        String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
        System.out.println(uploadPath);
        File uploadFile = new File(uploadPath);
        // 文件夹不存在就创建
        if (!uploadFile.exists()) {
            uploadFile.mkdir();
        }
        // 缓存:临时路径,文件超过预期大小,放到临时文件夹中,过几天自动删除,或者提醒用户转存为永久
        String tmpPath = this.getServletContext().getRealPath("WEB-INF/tmp");
        File file = new File(tmpPath);
        if (!file.exists()) {
            file.mkdir();
        }

        // 处理上传的文件,一般通过流来获取,可以使用request.getInputStream(),原生态的文件上传流获取,十分麻烦;
        // 建议使用 Apache的文件上传组件来实现,common-fileupload,它需要依赖于 commons-io组件;

        // 1、创建DiskFileItemFactory对象,处理文件上传路径或大小的限制
        DiskFileItemFactory factory = getDiskFileItemFactory(uploadFile);
        // 2、获取ServletFileUpload
        ServletFileUpload upload = getServletFileUpload(factory);
        // 3、处理上传的文件
        try {
            String msg = uploadParseRequest(upload, req, uploadPath);
            req.setAttribute("msg", msg);
            req.getRequestDispatcher("info.jsp").forward(req, resp);
        } catch (FileUploadException e) {
            e.printStackTrace();
        }
    }

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

    /**
     * 处理文件上传路径或大小的限制:文件大小、编码有默认设置
     *
     * @param file
     * @return
     */
    public static DiskFileItemFactory getDiskFileItemFactory(File file) {
        DiskFileItemFactory factory = new DiskFileItemFactory();
        // 工厂设置:缓冲区大小(上传的文件大于这个缓冲区的时,存放到临时文件中)
        // 缓冲区大小为1M,可以不设置,有默认设置
        factory.setSizeThreshold(1024 * 1024);
        // 临时文件目录,需要一个File
        factory.setRepository(file);
        return factory;
    }

    /**
     * 获取ServletFileUpload
     *
     * @param factory
     * @return
     */
    public static ServletFileUpload getServletFileUpload(DiskFileItemFactory factory) {
        ServletFileUpload upload = new ServletFileUpload(factory);
        // 可选部分,可不设置==========================
        // 监听文件上传进度
        upload.setProgressListener(new ProgressListener() {
            @Override
            // pBytesRead:已经读取到的文件大小
            // pContentLength:文件大小
            public void update(long pBytesRead, long pContentLength, int pItems) {
                System.out.println("总大小:" + pContentLength + "已上传" + pBytesRead);
            }
        });
        // 处理乱码问题
        upload.setHeaderEncoding("utf-8");
        // 设置单个文件的最大值 10M
        upload.setFileSizeMax(1024 * 1024 * 10);
        // 设置总共能够上传文件的大小
        upload.setSizeMax(1024 * 1024 * 10);
        // =====================================
        return upload;
    }

    /**
     * 处理上传的文件
     *
     * @param upload
     * @param req
     * @param uploadPath
     * @return
     * @throws FileUploadException
     * @throws IOException
     */
    public static String uploadParseRequest(ServletFileUpload upload, HttpServletRequest req,
                                            String uploadPath) throws FileUploadException, IOException {
        String msg = "";
        // 把前端请求解析,封装成一个FileItem对象(tomcat10 req需要处理,非则报错)
        List<FileItem> fileItems = upload.parseRequest(new Rc(req));
        for (FileItem fileItem : fileItems) {
            if (fileItem.isFormField()) {
                String name = fileItem.getFieldName();
                String value = fileItem.getString("UTF-8");
                System.out.println(name + ":" + value);
            } else {
                //****************************处理文件****************************
                // 拿到文件名字
                String uploadFileName = fileItem.getName();
                System.out.println("上传的文件名:" + uploadFileName);
                if (uploadFileName.trim().equals("") || uploadFileName == null) {
                    continue;
                }
                // 获得上传的文件名
                String fileName = uploadFileName.substring(uploadFileName.lastIndexOf("/") + 1);
                // 获得文件的后缀名
                String fileExName = uploadFileName.substring(uploadFileName.lastIndexOf(".") + 1);

                // 如果文件后缀名fileExName不是所需的直接return,不进行处理,告诉用户文件类型不对
                System.out.println("文件信息 [文件名:" + fileName + "---文件类型" + fileExName + "]");

                // UUID.randomUUID,随机生一个唯一识别的通用码
                // 网络传输中的东西,都需要序列化
                // pojo,实体类,如果想要在多个电脑运行,传输--->需要吧对象都序列化了
                // JNI=java Native Interface
                // implements Serializable :标记接口,JVM--->java栈 本地方法栈 native-->c++
                // 可以使用UUID(唯一识别通用码)保证文件名唯一(生成目录)
                String uuidPath = UUID.randomUUID().toString();
                //****************************处理文件完毕****************************
                // 设置存储路径:创建目录/UUID生成目录
                String realPath = uploadPath + "/" + uuidPath;
                // 给每个文件创建一个对应的文件夹
                File realPathFile = new File(realPath);
                if (!realPathFile.exists()) {
                    realPathFile.mkdir();
                }
                //****************************存放地址完毕*****************************
                // 获得文件上传的流
                InputStream inputStream = fileItem.getInputStream();
                // 创建一个文件输出流
                // realPath是真实的文件夹
                FileOutputStream fos = new FileOutputStream(realPath + "/" + fileName);
                // 创建一个缓冲区
                byte[] buffer = new byte[1024 * 1024];
                // 判断是否读取完毕
                int len = 0;
                while ((len = inputStream.read(buffer)) > 0) {
                    fos.write(buffer, 0, len);
                }
                // 关闭流
                fos.close();
                inputStream.close();
                msg = "文件上传成功";
                // 上传成功,清除临时文件
                fileItem.delete();
                //*************************文件传输完毕**************************
            }
        }
        return msg;
    }

    // tomcat10 处理req,非则报错
    static class Rc implements RequestContext {
        HttpServletRequest request = null;

        public Rc(HttpServletRequest request) {
            this.request = request;
        }

        @Override
        public String getCharacterEncoding() {
            return request.getCharacterEncoding();
        }

        @Override
        public String getContentType() {
            return request.getContentType();
        }

        @Override
        public int getContentLength() {
            return request.getContentLength();
        }

        @Override
        public InputStream getInputStream() throws IOException {
            return request.getInputStream();
        }
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>消息提示</title>
</head>
<body>
${msg}
</body>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                      https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
         version="5.0">
    <servlet>
        <servlet-name>UploadServlet</servlet-name>
        <servlet-class>com.study.servlet.UploadServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>UploadServlet</servlet-name>
        <url-pattern>/upload.do</url-pattern>
    </servlet-mapping>
</web-app>

二、文件下载

2.1 概述

// 二进制文件
resp.addHeader("Content-Type","application/octet-stream");
// 这是一个附件下载
resp.addHeader("Content-Disposition","attachment;filename="+filename);      

2.2 代码实现

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>文件下载</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/download?filename=1.xlsx">文件下载</a>
</body>
</html>
@WebServlet("/download")
public class DownloadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1.从前端获取下载文件名
        String fileName = req.getParameter("filename");
        // 2.获取下载路径
        String realPath = this.getServletContext().getRealPath("/download/" + fileName);
        // 3. 设置浏览器的下载支持(Content-Disposition),并将文件名转码,避免乱码
        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);
        }
        // 8.关闭流
        in.close();
        out.close();
    }

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

三、邮件发送

3.1 邮件的发送与接收原理

3.2 邮件的发送与接收过程

模拟邮件发送与接收:

3.3 代码实现邮件的发送

  1. 简单邮件发送(没有图片和附件)
package com.sutdy.mail;

import com.sun.mail.util.MailSSLSocketFactory;
import jakarta.mail.*;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;

import java.util.Properties;

public class MailDemo01 {
    public static void main(String[] args) throws Exception {
        Properties prop = new Properties();
        // 设置邮件服务器
        prop.setProperty("mail.host", "smtp服务器地址");
        // 邮件发送协议
        prop.setProperty("mail.transport.protocol", "smtp");
        // 需要验证用户密码
        prop.setProperty("mail.smtp.auth", "true");

        /* QQ邮箱需要设置SSL加密(其它邮箱不需要)
        MailSSLSocketFactory sf = new MailSSLSocketFactory();
        sf.setTrustAllHosts(true);
        prop.put("mail.smtp.ssl.enable", "true");
        prop.put("mail.smtp.ssl.socketFactory", sf);
        */

        // 使用javaMail发送邮件的6个步骤
        // 1.创建定义整个应用程序所需要的环境信息的session对象
         
        /* QQ邮箱需要,其它不需要
        Session session = Session.getDefaultInstance(prop, new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication("邮箱", "授权码");
            }
        });
        */

        Session session = Session.getDefaultInstance(prop);
        // 开启session的debug模式,这样可以查看到程序发送Email的运行状态
        session.setDebug(true);
        // 2.通过session得到transport对象
        Transport ts = session.getTransport();
        // 3.使用邮箱的用户名和密码连上邮件服务器(QQ邮箱:密码为 授权码)
        ts.connect("smtp服务器地址", "邮箱用户名", "邮箱密码");
        // 4.创建邮件:写文件
        // 注意需要传递session
        MimeMessage message = new MimeMessage(session);
        // 指明邮件的发件人
        message.setFrom(new InternetAddress("发件人邮箱"));
        // 指明邮件的收件人
        message.setRecipient(Message.RecipientType.TO, new InternetAddress("收件人邮箱"));
        // 邮件标题
        message.setSubject("标题");
        
        // 邮件的内容:文本(如有附件,图片等,只需更改此处内容)
        message.setContent("内容", "text/html;charset=UTF-8");
        
        // 5.发送邮件
        ts.sendMessage(message, message.getAllRecipients());
        // 6.关闭连接
        ts.close();
    }
}
  1. 复杂的邮件发送(带图片和附件)
// ===================邮件的内容:带图片和附件===================
// (1.1)准备图片内容
MimeBodyPart image = new MimeBodyPart();
// 图片需要经过数据化的处理
DataHandler dh = new DataHandler(new FileDataSource("图片路径"));
// 在part中放入,处理过图片的数据
image.setDataHandler(dh);
// 给这个part设置ID名字,后面通过 src的cid使用
image.setContentID("bz.jpg");

// (1.2)准备正文内容
MimeBodyPart text = new MimeBodyPart();
text.setContent("这是一张正文<img src='cid:bz.jpg'>", "text/html;charset=UTF-8");

// (1.3)准备附件内容
MimeBodyPart file = new MimeBodyPart();
file.setDataHandler(new DataHandler(new FileDataSource("文件路径")));
// 给附件设置别名
file.setFileName("1.txt");

// (2.1)描述数据关系
MimeMultipart multipart1 = new MimeMultipart();
multipart1.addBodyPart(image);
multipart1.addBodyPart(text);
// alternative 只能发送  文本
// related  发送文本 + 图片
// mixed    发送文本 + 图片 + 附件
multipart1.setSubType("related");

// (2.2)正文内容:图片+文本
MimeBodyPart contentText = new MimeBodyPart();
contentText.setContent(multipart1);

// (2.3)拼接所有内容:正文 + 附件
MimeMultipart allFile = new MimeMultipart();
// 添加附件
allFile.addBodyPart(file);
// 添加正文内容
allFile.addBodyPart(contentText);
allFile.setSubType("mixed");

// (3)设置到消息中,保存修改
// 把编辑好的邮件放到消息中
message.setContent(allFile);
// 保存修改
message.saveChanges();

// 5.发送邮件
ts.sendMessage(message, message.getAllRecipients());
// 6.关闭连接
ts.close();
  1. JavaWeb 发送邮件
<form action="${pageContext.request.contextPath}/RegisterServlet.do" method="post">
    <p>
        <label for="username">用户名:</label>
        <input type="text" name="username" id="username">
    </p>
    <p>
        <label for="pwd">密码:</label>
        <input type="text" name="password" id="pwd">
    </p>
    <p>
        <label for="email">邮箱:</label>
        <input type="text" name="email" id="email">
    </p>
    <p><input type="submit" value="注册"></p>
</form>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>注册成功</title>
</head>
<body>
${message}
</body>
</html>
public class Sendmail extends Thread {
    // 用于发送邮件的邮箱
    private String from = "发件邮箱";
    // 邮箱用户名
    private String username = "发件邮箱用户名";
    // 邮箱密码
    private String password = "发件邮箱密码";
    // 邮箱服务器 smtp
    private String host = "发件邮箱服务器";
    
    // 调用User类
    private User user;  
    public Sendmail(User user) {
        this.user = user;
    }

    @Override
    // 重写run方法,在run方法中发送邮件给指定用户
    public void run() {
        try {
            Properties prop = new Properties();
            // 设置邮件服务器
            prop.setProperty("mail.host", host);
            // 邮件发送协议
            prop.setProperty("mail.transport.protocol", "smtp");
            // 需要验证用户密码
            prop.setProperty("mail.smtp.auth", "true");

            /* QQ邮箱需要设置SSL加密(其它邮箱不需要)
               MailSSLSocketFactory sf = new MailSSLSocketFactory();
               sf.setTrustAllHosts(true);
               prop.put("mail.smtp.ssl.enable", "true");
               prop.put("mail.smtp.ssl.socketFactory", sf);
             */

            // 使用javaMail发送邮件的6个步骤
            // 1.创建定义整个应用程序所需要的环境信息的session对象

            /* QQ邮箱需要,其它不需要
                Session session = Session.getDefaultInstance(prop, new Authenticator() {
                    @Override
                    protected PasswordAuthentication getPasswordAuthentication() {
                        return new PasswordAuthentication("发件邮箱", "授权码");
                    }
                });
             */

            Session session = Session.getDefaultInstance(prop);
            // 开启session的debug模式,这样可以查看到程序发送Email的运行状态
            session.setDebug(true);
            // 2.通过session得到transport对象
            Transport ts = null;
            ts = session.getTransport();
            // 3.使用邮箱的用户名和授权码连上邮件服务器
            ts.connect(host, username, password);
            // 4.创建邮件:写文件
            // 注意需要传递session
            MimeMessage message = new MimeMessage(session);
            // 指明邮件的发件人
            message.setFrom(new InternetAddress(from));
            // 指明邮件的收件人:前端获取 user.getEmail()
            message.setRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));
            // 邮件标题
            message.setSubject("注册通知");

            // 邮件的文本内容(只需更换此处内容)
            String info = "恭喜您注册成功,您的用户名:" + user.getUsername() + " ,密码:" + user.getPassword();
            message.setContent(info, "text/html;charset=UTF-8");

            // 5.发送邮件
            ts.sendMessage(message, message.getAllRecipients());
            // 6.关闭连接
            ts.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
@WebServlet("/RegisterServlet.do")
public class RegisterServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 前端获取属性值
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String email = req.getParameter("email");
        // 创建User对象,存储属性值
        User user = new User(username, password, email);

        // 用户注册成功后,给用户发送邮件
        // 使用线程发送邮件,防止出现耗时和网站人多的情况
        Sendmail send = new Sendmail(user);
        // 启动线程,线程启动后就会执行run方法,发送邮件
        send.start();
        System.out.println("success");

        // 注册用户,响应页面
        req.setAttribute("message", "注册成功!已发送邮件,请注意查收");
        req.getRequestDispatcher("info.jsp").forward(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
  1. springboot 实现邮件发送
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>   
</dependency>
@SpringBootTest
class Demo1ApplicationTests {
    // 注入Java邮件发送包
    @Autowired
    JavaMailSenderImpl javaMailSender;

    // 普通文本邮件
    @Test
    void contextLoads() {
        // 发送邮件
        // 邮件接收人
        // 内容
        // 创建邮件消息
        SimpleMailMessage message = new SimpleMailMessage();
        // 设置主题
        message.setSubject("SpringBoot 发送邮件");
        // 设置文本内容
        message.setText("SpringBoot 邮件测试");
        // 发送者
        message.setFrom("发件人邮箱");
        // 接受者
        message.setTo("收件人邮箱");
        // 发送
        javaMailSender.send(message);
    }

    // 带附件的邮件
    @Test
    void test() throws Exception {
        // 带附件带图片  需要创建发送消息
        MimeMessage mimeMessage = javaMailSender.createMimeMessage();
        // 创建处理类   处理 mimeMessage   true  为更改编码
        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);

        helper.setSubject("SpringBoot 带附件的邮件");
        helper.setText("<h1>SpringBoot 带附件的邮件</h1>", true);

        // new File()  指向文件的路径 + 文件名字
        helper.addAttachment("测试文档.txt", new File("路径"));
        helper.addAttachment("测试图片.png", new File("路径"));
        helper.setFrom("发件人邮箱");
        helper.setTo("收件人邮箱");
        javaMailSender.send(mimeMessage);
    }
}
# 邮箱的用户名
spring.mail.username=邮箱的用户名
# 邮箱的密码  QQ邮箱为:授权码
spring.mail.password=邮箱的密码
# smtp服务器
spring.mail.host=smtp服务器
# 需要验证用户密码  设置为安全的
spring.mail.properties.mail.smtyp.ssl.enable=true
上一篇 下一篇

猜你喜欢

热点阅读