14. 文件传输原理及实现
14.1 导入需要的依赖
<!-- 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>
[文件上传的注意事项]
- 为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放WEB-INF目录下
- 为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名(防止用户文件上传发生覆盖解决办法:1. 加时间戳 2. uuid【生成随机数】 3. md5【加密】4. 位运算算法【自己写】)
- 要限制文件上传的最大值
- 可以限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法
【需要用到的类介绍】
SelvletFileUpload负责上传文件的数据,并将表单中每个输入项封装成一个FileItem对象,在使用ServletFileUpload对象解析请求时需要DiskFileItemFactory对象,所以,我们需要在进行解析工作前构造好Disk FIleItemFactory对象,通过ServletFileUpload对象的构造方法或setFileItemFactory()方法设置Servlet FileUpload对象的FileItemFactory属性
14.2 实例
前端设置:
-
主界面设置
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <body> <%-- GET:上传文件大小有限制 POST:上传文件大小没有限制 ${pageContext.request.contextPath} --%> <form action="upload.do" enctype="multipart/form-data" method="post"> 上传用户:<input type="text" name="username"><br/> <P><input type="file" name="file1"></P> <P><input type="file" name="file1"></P> <P><input type="submit" value="提交"> | <input type="reset"></P> </form> </body> </html>
-
提交成功页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page language="java" contentType="text/html;charset=UTF-8"%> <!DOCTYPE html> <html> <head> <title>Insert title here</title> </head> <body> <%=request.getAttribute("msg")%> </body> </html>
-
配置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"> <servlet> <servlet-name>FileServlet</servlet-name> <servlet-class>com.Yurrize.servlet.FileServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>FileServlet</servlet-name> <url-pattern>/upload.do</url-pattern> </servlet-mapping> </web-app>
后端设置:
-
判断表单是否需要上传文件
try{ // 判断上传的文件普通表单还是带文件的表单 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(); //创建这个月录 } // 创建上传文件的保存路径,建议在WEB-INF路径下,安全,用户无法直接访问上传的文件 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(file); // 2、获取ServletFileUpload ServletFileUpload upload = getServletFileUpload(factory); // 3、处理上传文件 // 把前端请求解析,封装成FileItem对象,需要从ServletFileUpload对象中获取 String msg = uploadParseRequest(upload, request, uploadPath);
-
创建DiskFileItemFactory对象,处理文件路径或者大小限制
public static DiskFileItemFactory getDiskFileItemFactory(File file) { DiskFileItemFactory factory = new DiskFileItemFactory(); // 通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区的时候,将他放到临时文件中; factory.setSizeThreshold(1024 * 1024);// 缓冲区大小为1M factory.setRepository(file);// 临时目录的保存目录,需要一个file return factory; }
-
获取ServletFileUpload
public static ServletFileUpload getServletFileUpload(DiskFileItemFactory factory) { ServletFileUpload upload = new ServletFileUpload(factory); // 监听上传进度 upload.setProgressListener(new ProgressListener() { // pBYtesRead:已读取到的文件大小enctype="multipart/form-data" // pContextLength:文件大小 public void update(long pBytesRead, long pContentLength, int pItems) { System.out.println("总大小:" + pContentLength + "已上传:" + pBytesRead+",进度:"+((double)pBytesRead/pContentLength)*100+"%"); } }); // 处理乱码问题 upload.setHeaderEncoding("UTF-8"); // 设置单个文件的最大值 upload.setFileSizeMax(1024 * 1024 * 10); // 设置总共能够上传文件的大小 // 1024 = 1kb * 1024 = 1M * 10 = 10м return upload; }
-
处理上传文件,把前端请求解析,封装成FileItem对象,需要从ServletFileUpload对象中获取
public static String uploadParseRequest(ServletFileUpload upload, HttpServletRequest request, String uploadPath) throws FileUploadException, IOException { String msg = ""; // 把前端请求解析,封装成FileItem对象 List<FileItem> fileItems = upload.parseRequest(request); for (FileItem fileItem : fileItems) { if (fileItem.isFormField()) {// 判断上传的文件是普通的表单还是带文件的表单 // getFieldName指的是前端表单控件的name; 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; } // 获得上传的文件名/images/girl/paojie.png String fileName = uploadFileName.substring(uploadFileName.lastIndexOf("/") + 1); // 获得文件的后缀名 String fileExtName = uploadFileName.substring(uploadFileName.lastIndexOf(".") + 1); /* * 如果文件后缀名fileExtName不是我们所需要的 就直按return.不处理,告诉用户文件类型不对。 */ System.out.println("文件信息[件名: " + fileName + " ---文件类型" + fileExtName + "]"); // 可以使用UID(唯一识别的通用码),保证文件名唯 // 0UID. randomUUID(),随机生一个唯一识别的通用码; String uuidPath = UUID.randomUUID().toString(); // ================处理文件完毕============== // 存到哪? uploadPath // 文件真实存在的路径realPath String realPath = uploadPath + "/" + uuidPath; // 给每个文件创建一个对应的文件夹 File realPathFile = new File(realPath); if (!realPathFile.exists()) { realPathFile.mkdir(); } // ==============存放地址完毕============== // 获得文件上传的流 InputStream inputStream = fileItem.getInputStream(); // 创建一个文件输出流 // realPath =真实的文件夹; // 差了一个文件;加上翰出文件的名产"/"+uuidFileName FileOutputStream fos = new FileOutputStream(realPath + "/" + fileName); // 创建一个缓冲区 byte[] buffer = new byte[1024 * 1024]; // 判断是否读取完毕 int len = 0; // 如果大于0说明还存在数据; while ((len = inputStream.read(buffer)) > 0) { fos.write(buffer, 0, len); } // 关闭流 fos.close(); inputStream.close(); msg = "文件上传成功!"; fileItem.delete(); // 上传成功,清除临时文件 //=============文件传输完成============= } } return msg; }
-
Servlet请求转发消息
// Servlet请求转发消息 System.out.println(msg); if(msg == "文件上传成功!") { // Servlet请求转发消息 request.setAttribute("msg",msg); request.getRequestDispatcher("info.jsp").forward(request, response); }else { msg ="请上传文件"; request.setAttribute("msg",msg); request.getRequestDispatcher("info.jsp").forward(request, response); } } catch (FileUploadException e) { e.printStackTrace(); }
15. 邮件发送原理及实现
发送邮件协议:SMTP协议
接收邮件协议:POP3协议
使用Java发送邮件,需要准备JavaMail API和 Java Activation Framework
-
JavaMail API
<!-- https://mvnrepository.com/artifact/javax.mail/mail --> <dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> <version>1.4.7</version> </dependency>
-
Java Activation Framework
<!-- https://mvnrepository.com/artifact/javax.activation/activation --> <dependency> <groupId>javax.activation</groupId> <artifactId>activation</artifactId> <version>1.1.1</version> </dependency>
实例:
package com.Yurrize;
import com.sun.mail.util.MailSSLSocketFactory;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.security.GeneralSecurityException;
import java.util.Properties;
public class MailDemo01 {
public static void main(String[] args) throws GeneralSecurityException, MessagingException {
Properties properties = new Properties();
properties.setProperty("mail.host","smtp.qq.com");//设置qq邮件服务器
properties.setProperty("mail.transport.protocol","smtp");//邮件发送协议
properties.setProperty("mail.smtp.auth","true");//需要验证用户名密码
//关于qq邮箱需要设置SSL加密,其他邮箱不需要,大厂
MailSSLSocketFactory mailSSLSocketFactory = new MailSSLSocketFactory();
mailSSLSocketFactory.setTrustAllHosts(true);
properties.put("mail.smtp.ssl.enable",true);
properties.put("mail,smtp,ssl,socketFactory",mailSSLSocketFactory);
//使用Java发送邮件的5个步骤
//1.创建定义整个应用程序所需的环绕信息的Session对象
//创建定义整个应用程序所需的环境信息的Session对象
//QQ才有,其他邮箱不用
Session session=Session.getDefaultInstance(properties, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("E-mail","key");
}
});
//开启session的debug模式,这样就可以看到程序发送Email的运行状态
session.setDebug(true);
//2.通过Session得到transport对象
Transport transport = session.getTransport();
//3.使用邮箱的用户名和授权码连上邮箱服务器
transport.connect("smtp.qq.com","E-mail","key");
//4.创建邮箱:写邮件
//注意需要传递Session
MimeMessage message = new MimeMessage(session);
//指明邮箱发件人
message.setFrom(new InternetAddress("1204456239@qq.com"));
//指明邮件的收件人,若发件人和收件人一致,则是自己发给自己
message.setRecipient(Message.RecipientType.TO,new InternetAddress("E-mail"));
//邮件的标题
message.setSubject("Yurrize发的!!!!!");
//邮件的文本内容
message.setContent("你好呀","text/html;charset=UTF-8");
//5.发送邮箱
transport.sendMessage(message,message.getAllRecipients());
//6.关闭邮件
transport.close();
}
}
例子:实现发送注册邮箱
-
前端页面编写
注册页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <body> <%-- GET:上传文件大小有限制 POST:上传文件大小没有限制 ${pageContext.request.contextPath} --%> <form action="${pageContext.request.contextPath}/RegisterServlet.do" method="post"> 用户名:<input type="text" name="username"><br/> 密码:<input type="password" name="password"><br/> 邮箱:<input type="text" name="email"><br/> <input type="submit" value="注册"> </form> </body> </html>
成功页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>网站温馨提示</h1> ${message} </body> </html>
-
异步实现邮箱发送,加快发送效率
package com.Yurrize.utils; //多线程实现邮件发送 //使用多线程的原因就是提高用户的体验,一旦一个页面3s及以上的时间白屏就可能被用户关掉 //所以我们在用户提交表单之后,将费时的邮件发送工作使用一个子线程来完成,而主线程还是去完成它自己的事情 //这么做就可以提高用户体验,不然用户等待邮件发送的时间 import com.Yurrize.pojo.User; import com.sun.mail.util.MailSSLSocketFactory; import javax.mail.*; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import java.security.GeneralSecurityException; import java.util.Properties; //多线程这种处理就可以称为异步处理 public class Sendmail implements Runnable{ private String from = "E-mail";//我们用来向客户发送邮件的邮箱 private String username = "E-mail";//用于登陆SMTP服务器的用户名 private String password = "key";//授权码 private User user; public Sendmail(User user) { this.user = user;//用于获取用户邮箱地址 } @Override public void run() { try { Properties prop = new Properties(); prop.setProperty("mail.host", "smtp.qq.com"); //设置QQ邮件服务器 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发送邮件的5个步骤 //1、创建定义整个应用程序所需的环境信息的 Session 对象 //使用QQ邮箱的时候才需要,其他邮箱不需要这一段代码 Session session = Session.getDefaultInstance(prop, new Authenticator() {//获取和SMTP服务器的连接对象 public PasswordAuthentication getPasswordAuthentication() { //发件人邮件用户名、授权码 return new PasswordAuthentication(username, password); } }); //开启Session的debug模式,这样就可以查看到程序发送Email的运行状态 session.setDebug(true); //2、通过session得到transport对象 Transport ts = session.getTransport();//通过这一次和SMTP服务器的连接对象获取发送邮件的传输对象 //3、使用邮箱的用户名和授权码连上SMTP邮件服务器,即登陆 ts.connect("smtp.qq.com", username, password); //4、创建邮件对象MimeMessage——点击网页上的写信 //创建一个邮件对象 MimeMessage message = new MimeMessage(session); //指明邮件的发件人——在网页上填写发件人 message.setFrom(new InternetAddress(username));//设置发件人 //指明邮件的收件人,现在发件人和收件人是一样的,那就是自己给自己发——在网页上填写收件人 message.setRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));//设置收件人 //邮件的标题——在网页上填写邮件标题 message.setSubject("欢迎注册thhh!");//设置邮件主题 //邮件的文本内容——在网页上填写邮件内容 message.setContent("<p><h2>恭喜注册成功!</h2></p >您的用户名为:<h4>"+user.getUsername()+"</h4>请妥善保管您的密码,如有问题请及时联系网站客服,再次欢迎您的加入!!", "text/html;charset=UTF-8");//设置邮件的具体内容 //5、发送邮件——在网页上点击发送按钮 ts.sendMessage(message, message.getAllRecipients()); //6、关闭连接对象,即关闭服务器上的连接资源 ts.close(); } catch (MessagingException e) { e.printStackTrace(); } catch (GeneralSecurityException e) { e.printStackTrace(); } } }
-
Servlet类编写
package com.Yurrize.servlet; import com.Yurrize.pojo.User; import com.Yurrize.utils.Sendmail; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class RegisterServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1、接收用户填写的表单数据 String username = req.getParameter("username"); String password = req.getParameter("password"); String email = req.getParameter("email"); // System.out.println(username+password+email); //2、向用户邮箱发送邮件,注意发送邮件很耗时,所以我们启动一个子线程去做这件事,而用户则是直接跳转注册成功页面,以免降低用户体验 User user = new User(username,password,email); Sendmail sendmail = new Sendmail(user);//获取子线程对象 new Thread(sendmail).start();//启动子线程 //3、视图跳转 req.setAttribute("message","注册成功!我们已经向您的邮箱发送了邮件,请您及时进行查收。由于网络原因,您收到邮件的时间存在延迟,敬请谅解~"); req.getRequestDispatcher("info.jsp").forward(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
16.Axios框架
Axios是一个基于promise的网络请求库
- 引入axios的js文件
- 使用axios发送请求,并获取响应结果
axios.提交方法.then(funtion)…
17.Json
概念:JavaScript Object Notation 。JavaScript对象表示法
ication(username, password);
}
});
//开启Session的debug模式,这样就可以查看到程序发送Email的运行状态
session.setDebug(true);
//2、通过session得到transport对象
Transport ts = session.getTransport();//通过这一次和SMTP服务器的连接对象获取发送邮件的传输对象
//3、使用邮箱的用户名和授权码连上SMTP邮件服务器,即登陆
ts.connect("smtp.qq.com", username, password);
//4、创建邮件对象MimeMessage——点击网页上的写信
//创建一个邮件对象
MimeMessage message = new MimeMessage(session);
//指明邮件的发件人——在网页上填写发件人
message.setFrom(new InternetAddress(username));//设置发件人
//指明邮件的收件人,现在发件人和收件人是一样的,那就是自己给自己发——在网页上填写收件人
message.setRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));//设置收件人
//邮件的标题——在网页上填写邮件标题
message.setSubject("欢迎注册thhh!");//设置邮件主题
//邮件的文本内容——在网页上填写邮件内容
message.setContent("<p><h2>恭喜注册成功!</h2></p >您的用户名为:<h4>"+user.getUsername()+"</h4>请妥善保管您的密码,如有问题请及时联系网站客服,再次欢迎您的加入!!", "text/html;charset=UTF-8");//设置邮件的具体内容
//5、发送邮件——在网页上点击发送按钮
ts.sendMessage(message, message.getAllRecipients());
//6、关闭连接对象,即关闭服务器上的连接资源
ts.close();
} catch (MessagingException e) {
e.printStackTrace();
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
}
}
3. Servlet类编写
```java
package com.Yurrize.servlet;
import com.Yurrize.pojo.User;
import com.Yurrize.utils.Sendmail;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class RegisterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、接收用户填写的表单数据
String username = req.getParameter("username");
String password = req.getParameter("password");
String email = req.getParameter("email");
// System.out.println(username+password+email);
//2、向用户邮箱发送邮件,注意发送邮件很耗时,所以我们启动一个子线程去做这件事,而用户则是直接跳转注册成功页面,以免降低用户体验
User user = new User(username,password,email);
Sendmail sendmail = new Sendmail(user);//获取子线程对象
new Thread(sendmail).start();//启动子线程
//3、视图跳转
req.setAttribute("message","注册成功!我们已经向您的邮箱发送了邮件,请您及时进行查收。由于网络原因,您收到邮件的时间存在延迟,敬请谅解~");
req.getRequestDispatcher("info.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
16.Axios框架
Axios是一个基于promise的网络请求库
- 引入axios的js文件
- 使用axios发送请求,并获取响应结果
axios.提交方法.then(funtion)…
17.Json
概念:JavaScript Object Notation 。JavaScript对象表示法