TestServlet.java
// 文件路径 D:\ApacheServer\web_java\HelloWorld\src\com\test\TestServlet.java
package com.test;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.SQLException;
import java.sql.PreparedStatement;
import java.text.SimpleDateFormat;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.apache.commons.fileupload.FileUploadException;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
// 由于 web.xml 里配置的 url-pattern = /TomcatTest/TestServlet ,所以该 Servlet 浏览地址可以是 http://localhost:8080/TomcatTest/TestServlet
// 下面的注解 @WebServlet 功能和 web.xml 配置 url-pattern 类似,所以该 Servlet 浏览地址也可以是 http://localhost:8080/TestServlet
// 注解浏览地址和 web.xml 里配置的 url-pattern 地址不能一样,要么只配置其中一项(删除注解,或者删除web.xml中对应Servlet的<servlet-mapping>...</servlet-mapping>项),要么两者配置地址不能相同
@WebServlet("/TestServlet")
public class TestServlet extends HttpServlet {
// 实际作用不明,暂时注释,无影响
//private static final long serialVersionUID = 1L;
public TestServlet() {
super();
}
public void init() throws ServletException {
// 创建 Servlet 时只执行一次的 init
}
public void destroy() {
// 销毁 Servlet 时只执行一次的 destroy
// destroy 方法被调用后,servlet 被销毁,但是并没有立即被回收,再次请求时,并没有重新初始化。
}
// post 请求会被 doPost 处理
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 转移到 doGet 函数处理信息
doGet(request, response);
}
// get 请求会被 doGet 处理
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//response.getWriter().append("Served at1: ").append(request.getContextPath());
/*
// 配合测试异常处理 Servlet (这里是 TestErrorServlet )捕获处理 web 容器抛出的异常
if(true) {
response.setContentType("text/html;charset=UTF-8");
throw new ServletException("这是测试异常信息");
}
*/
// ============================= response 响应 ============================================
// 设置返回响应内容类型及编码,否则返回输出中文会乱码
response.setContentType("text/html;charset=UTF-8");
// 添加一个自定义名称和值的响应报头。
response.setHeader("test_foo", "test_val");
// 响应返回任意错误响应状态码及信息(通常为404 或407等状态码),该设置会使页面直接显示错误页面
//response.sendError(404,"test response status");
// 返回任意响应状态码
//response.setStatus(200);
// 生成一个 302 响应,暂时性重定向到后面的网址
//response.sendRedirect("http://www.baidu.com");
// ============================= 请求的 HTTP 头信息 ============================================
// 获取打印客户端请求的 HTTP 头信息
Enumeration headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()) {
String paramName = (String)headerNames.nextElement();
String paramValue = request.getHeader(paramName);
response.getWriter().append("<br/>客户端请求的 HTTP 头信息 : " + paramName + " 值为 : " + paramValue);
}
// ============================= Servlet Cookie ============================================
// Servlet Cookie 处理需要对中文进行编码与解码,方法如下
String enStr = java.net.URLEncoder.encode("测试中文", "UTF-8"); // 编码
String deStr = java.net.URLDecoder.decode(enStr, "UTF-8"); // 解码
// 创建新 cookie 对象 并赋键值对,键值即该 cookie 名称,这里值赋中文值,需要先编码
Cookie cookie = new Cookie("testKey",enStr);
// 设置 cookie 适用的域名,例如 runoob.com
cookie.setDomain("runoob.com");
// 获取 cookie 适用的域,例如 runoob.com
cookie.getDomain();
// 设置 cookie 过期的时间(以传给客户端为起始,以秒为单位)。如果不设置,cookie 只会在当前 session 会话中持续有效。或者cookie.setMaxAge(0) 即表示删除这个cookie
cookie.setMaxAge(3600 * 24);
// 返回 cookie 的最大生存周期(以秒为单位),默认情况下,-1 表示 cookie 将持续下去,直到浏览器关闭。
cookie.getMaxAge();
// 返回 cookie 的名称。名称在创建后不能改变。
cookie.getName();
// 设置 cookie 的值,除了创建 cookie 对象时赋值,setValue() 方法也可赋值。赋的值非中文就不用转码了
cookie.setValue("newTestVal");
// 获取 cookie 的值
cookie.getValue();
// 设置 cookie 适用的路径uri。浏览器在发该 cookie 消息给服务器之前,请求的 url 中必须存在一个指定 uri 路径。这个比较是通过将 path 属性值与请求的 url 从头开始逐字符串比较完成的。如果字符匹配,则发送该 cookie 消息。如果不指定路径,与当前页面相同目录(uri)下的(包括子目录下的)所有 url 浏览器都会返回 cookie。
cookie.setPath("/testuri");
// 获取 cookie 适用的路径uri。
cookie.getPath();
// 设置布尔值,表示 cookie 是否应该只在加密的(即 SSL)连接上发送。
cookie.setSecure(false);
// 设置该 cookie 在浏览器中不能通过 Javascript 的 document.cookie 属性访问
cookie.setHttpOnly(true);
// 设置cookie的注释。该注释在浏览器向用户呈现 cookie 时非常有用。
cookie.setComment("testNotes");
// 获取 cookie 的注释,如果 cookie 没有注释则返回 null。
cookie.getComment();
// 输出 cookie值,中文的话需要解码
response.getWriter().append("<br/>cookie testKey 值为 : " + java.net.URLDecoder.decode(cookie.getValue(), "UTF-8"));
// 发送 Cookie 到 HTTP 响应头,即将 cookie 传到浏览器
response.addCookie(cookie);
// 获取览器发送的与当前页面 url 相关的 Cookie 的数组
Cookie request_cookie[] = request.getCookies();
if( request_cookie != null ){
// 遍历获取每一个浏览器发送的 cookie
for (int i = 0; i < request_cookie.length; i++){
cookie = request_cookie[i];
// 对比找到指定名称的 cookie
if((cookie.getName( )).compareTo("delCookieName") == 0 ){
// 然后删除
cookie.setMaxAge(0);
response.addCookie(cookie);
}else {
response.getWriter().append("<br/>浏览器上传 cookie 名称 : " + cookie.getName() + " 值为 : " + cookie.getValue());
}
}
}else{
response.getWriter().append("<br/>No Cookie founds");
}
// ============================= Servlet Session ============================================
// 获取当前请求的 session 会话,需要在向客户端发送任何文档内容之前调用 request.getSession()。
// getSession() 等同于 getSession(true) 若存在会话,则返回该会话,否则新建一个会话。getSession(false) 则是如存在会话,则返回该会话,否则返回NULL
HttpSession session = request.getSession();
//下面总结了 HttpSession 对象中可用的几个重要的方法:
// 指定键名绑定一个对象到该 session 会话,绑定的对象可以是任意类型的对象包括字符串
Object anyObject = new Object();
session.setAttribute("testKey", anyObject);
// 获取 session 中指定键名的数据值,如果没有指定该键名对则返回 null,之前赋的键值无论何类型,getAttribute 方法返回的都是 Object 类型,使用时需要强制类型
Object val = session.getAttribute("testKey");
// 从该 session 会话移除指键名称及对应对象。
session.removeAttribute("testKey");
// 以枚举方式获取 session 中所有设置绑定的键名
Enumeration sessionEnumeration = session.getAttributeNames();
// 获取 session id
String sessionId = session.getId();
// 返回该 session 创建的时间戳,单位毫秒,创建完不一定已经回传给客户端。
long sessionBegin = session.getCreationTime();
// 判断该对话是否是第一次建立,即客户端还未保存 session id,或者客户选择不参入该 session 会话,则该方法返回 true
boolean isNew = session.isNew();
// 返回该 session 客户端最后一次请求到服务器的时间戳,单位毫秒
long sessionEnd = session.getLastAccessedTime();
// 设定 session 在用户请求间隔多少秒内不失效
session.setMaxInactiveInterval(360);
// 返回 Servlet 容器 session 在用户请求间隔多少秒内不失效
int interval = session.getMaxInactiveInterval();
// 使该 session 会话无效,并解除绑定到它上面的任何对象。
//session.invalidate();
// 输出相关信息
// session 创建时间
Date createTime = new Date(sessionBegin);
// 该网页的最后一次访问时间
Date lastAccessTime = new Date(sessionEnd);
//设置日期输出的格式
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 设置键值
String testStr = "";
int testInt = 0;
if(session.getAttribute("key_str") == null) {
testStr = "this is test str";
session.setAttribute("key_str", testStr);
}else {
testStr = (String)session.getAttribute("key_str");
}
if(session.getAttribute("key_int") == null) {
testInt = 0;
session.setAttribute("key_int", testInt);
}else {
testInt = (int)session.getAttribute("key_int") + 1;
session.setAttribute("key_int", testInt);
}
response.getWriter().append("<br/>用户最后一次请求时间为 : " + dateFormat.format(lastAccessTime));
response.getWriter().append("<br/>session key_str 键对应值为 : " + testStr);
response.getWriter().append("<br/>session testInt 键对应值为 : " + testInt);
/*
// ============================= 下载文件 ============================================
// 最好放在 response 所有输出之前调用
this.download(response);
// 为禁止再向浏览器发送其他输出流
if(true) return;
*/
// ============================= 连接 MySql ============================================
this.conn_mysql(response);
// ============================= 日期处理 ============================================
doDate(response);
// ============================= 网页重定向 ============================================
// 浏览器重新请求的新地址,URL会变
String location = "https://www.baidu.com" ;
// 方法一
//response.sendRedirect(location);
// 方法二
//response.setStatus(response.SC_MOVED_TEMPORARILY);
//response.setHeader("Location", location);
// ============================= 自动刷新页面 ============================================
// 设置自动刷新间隔为 5 秒
//response.setIntHeader("Refresh", 5);
// ============================= 发送邮件 ============================================
sendMail(request, response);
// 获取 form-data 类型 post 数据依赖于 FileUpload,下载地址 http://commons.apache.org/proper/commons-fileupload/ 这里用到的是 FileUpload 1.4 选择 Binaries->commons-fileupload-1.4-bin.zip
// FileUpload 依赖于 Commons IO,下载地址 http://commons.apache.org/proper/commons-io/ 这里用到的是 Commons IO 2.6 选择 Binaries->commons-io-2.6-bin.zip
// 将下载的压缩包内的 commons-io-2.6.jar 和 commons-fileupload-1.4.jar 解压缩到 D:\ApacheServer\web_java\HelloWorld\WebContent\WEB-INF\lib 中。环境变量 CLASSPATH 补充 ";D:\ApacheServer\web_java\HelloWorld\WebContent\WEB-INF\lib\commons-io-2.6.jar;D:\ApacheServer\web_java\HelloWorld\WebContent\WEB-INF\lib\commons-fileupload-1.4.jar;" 。eclipse->Java Build Path 中分别引入 commons-io-2.6.jar 与 commons-fileupload-1.4.jar
// 检测是 GET 和 x-www-form-urlencode POST 还是 multipart/form-data POST 方式
if (!ServletFileUpload.isMultipartContent(request)) {
// 提交的表单类型为GET 或者 x-www-form-urlencoded 方式的 POST
getOrFormUrlencoded(request, response);
}else {
// 提交的表单类型为 multipart/form-data 的 POST
formData(request, response);
}
}
// 自建方法,文件下载
public void download(HttpServletResponse response){
try{
// 要下载的文件名,必须是服务器上存在的文件,eclipse 实际运行项目路径需要找找
// 这里实际目录为 D:/ApacheServer/web_java/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/HelloWorld/upload/00125943U-0.jpg
String fileName = "00125943U-0.jpg";
// 文件下载到客户端时文件名
String downName = "download.jpg";
// 设置文件 MIME 类型,getServletContext()为父类方法
response.setContentType(this.getServletContext().getMimeType(fileName));
// 设置 Content-Disposition 响应报头,命名下载文件名称
response.setHeader("Content-Disposition"," attachment;filename=" + downName);
ServletContext context = this.getServletContext();
// 根据文件在项目中的路径获取文件的完整绝对路径,这里 upload 文件夹为项目根目录下,与 WEB-INF 文件夹同级父目录下
String fullFileName = context.getRealPath("/upload/" + fileName);
// 把文件读入到内存输入流中
InputStream inputStream = new FileInputStream(fullFileName);
// 创建输出流对象
ServletOutputStream outputStream = response.getOutputStream();
// 每次从输入流实际读取到的字节数
int len = 0;
// 定义一个字节数组,相当于缓存,数组长度为1024,即缓存大小为1024个字节
byte[] cache = new byte[1024];
// inputStream.read(cache)) 方法,从输入流中读取最多 cache 数组大小的字节,并将其存储在 cache 中。以整数形式返回实际读取的字节数,当文件读完时返回-1
while((len = inputStream.read(cache)) != -1){
// 每次把数组 cache 从 0 到 len 长度的内容输出到浏览器
outputStream.write(cache, 0, len);
}
// 关闭流
inputStream.close();
outputStream.close();
}catch(Exception exception) {
// 处理 Class.forName 错误
exception.printStackTrace();
}
}
// 自建方法,连接 MySQL 数据库
public void conn_mysql(HttpServletResponse response) {
// 需要下载 MySQL 驱动 jar 包,下载地址 https://mvnrepository.com/artifact/mysql/mysql-connector-java,这里用到的是 mysql-connector-java-8.0.17.jar,下载的 jar 包 要放在 Tomcat 安装目录下的 lib 文件夹内,这里路径是 D:\ApacheServer\apache-tomcat\lib\mysql-connector-java-8.0.17.jar
// 连接 MySQL 需要 MySQL 的时区和 java 一致(MySQL 默认 UTC 时区),这里本地是北京时区。修改 MySQL 配置文件 my.ini 在 [mysqld] 项下添加一行 default-time-zone = '+8:00' 重启 MySQL 即可。如果不能修改MySQL配置文件,则将如下 jdbc:mysql://localhost:3306/testdb 改为 jdbc:mysql://localhost:3306/testdb?serverTimezone=UTC 亦可
try{
// 注册 JDBC 驱动器
// 如果下载的 MySQL jar 包是 5 系列版本,则需要用 com.mysql.jdbc.Driver 注册驱动,而且必须写
//Class.forName("com.mysql.jdbc.Driver");
// MySQL jar包 6 以上版本用 com.mysql.cj.jdbc.Driver 注册,或者不写也可,这里没有写,已注释掉
//Class.forName("com.mysql.cj.jdbc.Driver");
// 打开一个连接
Connection conn = null;
// localhost 为数据库连接地址,3306 端口号,testDB 为数据库名称
String db_host = "jdbc:mysql://localhost:3306/testdb";
// 用户名
String user = "root";
// 密码
String password = "123456";
conn = DriverManager.getConnection(db_host,user,password);
// ==================================================== Statement 类处理
// Statement 对象执行查询 SQL 语句
Statement statement = conn.createStatement();
String sql_sel = "SELECT id, name, age FROM test_table";
ResultSet result = statement.executeQuery(sql_sel);
//遍历结果集
int i = 1;
while(result.next()){
int id = result.getInt("id");
String name = result.getString("name");
int age = result.getInt("age");
response.getWriter().append("<br/>第 " + i + "行 id : " + id + " name : " + name + " age : " + age);
i++;
}
// ==================================================== PreparedStatement 类处理
// PreparedStatement 执行插入语句效果更好,原因:1可以写动态参数化的查询,2PreparedStatement比 Statement 更快,3PreparedStatement可以防止SQL注入式攻击
// preparedStatement对象的 executeUpdate() 方法可执行 insert,update,delete 语句以及 SQL DDL(如建表,改表,删除表等), executeQuery() 方法执行select语句,execute()执行所有语句,效率也最慢
// ============================ executeUpdate 方法
//编写预处理 SQL 语句
String sql_ins= "INSERT INTO `test_table`(id, name, age) VALUES(?, ?, ?)";
PreparedStatement preparedStatement = conn.prepareStatement(sql_ins);
preparedStatement.setInt(1, 3); //插入表数据的 id
preparedStatement.setString(2, "testName"); // 名称
preparedStatement.setInt(3, 40); // 年龄
// 返回值是一个整数,指示受影响的行数(即更新计数)建表,删表等不操作行的语句总返回 0。
int updateCount = preparedStatement.executeUpdate();
response.getWriter().append("<br/> 插入结果 " + updateCount);
// ============================ executeQuery 方法
// 执行 select 语句
preparedStatement = conn.prepareStatement(sql_sel);
// 获取查询结果,result 遍历查询结果与之前 Statement 类方式一致
result = preparedStatement.executeQuery();
// ============================ execute 方法
preparedStatement = conn.prepareStatement(sql_sel);
// execute 可用于执行任何SQL语,返回一个 boolean 值,如果执行后第一个结果(有可能返回的是多个结果集合)是 ResultSet(即查询语句结果),则返回 true,否则返回 false。
boolean isResult = preparedStatement.execute();
// 如果 execute() 执行的 sql 语句能返回多个结果集合时,PreparedStatement 对象获取下个 getResultSet() 或者 getUpdateCount() 前要先执行 getMoreResults() 使指针下移,然后再执行 getResultSet() 或 getUpdateCount() 获取当前指针指向结果
//preparedStatement.getMoreResults(); // 一至多个结果集时,获取第一个结果时不需要执行此语句,获取下一个结果时才执行,如在 while 循环语句里用
if(isResult) {
// 如果结果集当前指针指向的返回结果是个 select 查询结果,用 getResultSet 获取,如果执行的是更新语句,则返回的是更新计数,这时就要用 getUpdateCount来获取
result = preparedStatement.getResultSet();
// ...
}else {
//当某个过程返回两个更新计数,则首先调用方法getUpdateCount()
updateCount = preparedStatement.getUpdateCount();
response.getWriter().append("<br/> 插入结果 " + updateCount);
}
// 完成后关闭
preparedStatement.close();
result.close();
statement.close();
conn.close();
}catch(SQLException sqlException) {
// 处理 JDBC 错误
sqlException.printStackTrace();
}catch(Exception exception) {
// 处理 Class.forName 错误
exception.printStackTrace();
}
}
// 自建方法,处理日期
public void doDate(HttpServletResponse response) throws ServletException, IOException {
Date date = null;
// 构造函数初始化当前日期和时间的对象
date = new Date();
// 或者构造指定日期时间的对象。参数为毫秒时间戳,示例时间为 2019/8/18 16:12:56
date = new Date(1566115976000L);
// date 对应的时间戳大于 after() 的参数所对应时间戳则返回 true,否则返回 false。这里返回 false
if(date.after(new Date())) {
response.getWriter().append("<br/> date对应时间大于 after 参数对应时间");
}else {
response.getWriter().append("<br/> date对应时间小于 after 参数对应时间");
}
// 判断方式与 date.after() 相反
boolean isBefore = date.before(new Date());
// 克隆一个时间对象,但返回值类型为 Object
Object objDate = date.clone();
// 如果两个 date 对象时间戳相等,返回0,如果 date 大于 compareTo() 里参数的时间,则返回正数,否则返回负数,这里结果为 -1
int compare = date.compareTo(new Date());
response.getWriter().append("<br/> 对比时间结果为 : " + compare);
// 两日期对象对应时间戳相等返回 true,否则返回 false,这里返回 true
boolean equals = date.equals(new Date(1566115976000L));
// 获取当前 date 对应时间戳
long stampLong = date.getTime();
response.getWriter().append("<br/> 当前 date 时间戳为 : " + stampLong);
// 返回该日期对象的哈希码值
int hashCode = date.hashCode();
// 设置 date 对象对应的时间戳
date.setTime(1566119343000L);
// 将 date 对象转换为字符串,这里返回为 Sun Aug 18 17:09:03 CST 2019
String strDate = date.toString();
response.getWriter().append("<br/> 当前 date 字符串为 : " + strDate);
// 创建日期格式化对象,y 年,M 月,d 月中第几日,H 小时(24制),h 小时(12制), m 分,s 秒,S 毫秒,E 星期几,D 年中第几日,F 月中第几周,w 年中第几周,W 月中的第几周,a 上午下午,k 天中第几小时,K 带有 A.M./P.M. 的小时(0~11),z 时区
SimpleDateFormat dateFormat = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss");
String strFormat = dateFormat.format(date);
// 这里输出日期为 2019-08-18 17:09:03
response.getWriter().append("<br/> 格式化后时间为 : " + strFormat);
}
// 自建方法,发送邮件
public void sendMail(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 需要先下载 javax.mail.jar 和 activation.jar。只发文字邮件 javax.mail.jar 就够了,发附件则还需要 activation.jar
// mail.jar 下载地址 https://www.oracle.com/technetwork/java/javamail/index.html 点击 Downloads 下载 JavaMail API 1.4.7,或者 https://javaee.github.io/javamail/ 下载 JavaMail 1.6.2 这里版本是 JavaMail 1.6.2
// jaf 下载页面 https://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-java-plat-419418.html 这里用到版本 JavaBeans Activation Framework 1.1.1
// 提取下载的压缩包里的 javax.mail.jar 和 activation.jar 放到项目中
// 收件人的电子邮件 ID
String to = "32705317@qq.com";
// 发件人的电子邮件 ID
String from = "zdy_521@126.com";
// SMTP服务器地址
String host = "smtp.126.com";
// 授权密码
String passWord = "87477zhang";
// 设置邮件发送相关属性
Properties properties = System.getProperties();
// 设置邮件传输采用的协议smtp(这里使用网易的smtp服务器)
properties.setProperty("mail.transport.protocol", "smtp");
//设置发送邮件的邮件服务器的属性
properties.setProperty("mail.smtp.host", host);
//需要经过授权,也就是有户名和密码的校验,这样才能通过验证(一定要有这一条)
properties.setProperty("mail.smtp.auth", "true");
// SMTP 服务器的端口 (非 SSL 连接的端口一般默认为 25, 可以不添加, 如果开启了 SSL 连接,
// 需要改为对应邮箱的 SMTP 服务器的端口, 具体可查看对应邮箱服务的帮助,
// QQ邮箱的SMTP(SLL)端口为465或587, 其他邮箱自行去查看)
/*
final String smtpPort = "465";
properties.setProperty("mail.smtp.port", smtpPort);
properties.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
properties.setProperty("mail.smtp.socketFactory.fallback", "false");
properties.setProperty("mail.smtp.socketFactory.port", smtpPort);
*/
// 获取默认的 Session 对象
Session session = Session.getDefaultInstance(properties);
// 会话采用debug模式
session.setDebug(true);
try {
// 创建邮件对象
MimeMessage message = new MimeMessage(session);
// 设置发送邮件地址,param1 代表发送地址 param2 代表发送的名称(任意的) param3 代表名称编码方式
message.setFrom(new InternetAddress(from, "发件人名称", "utf-8"));
// 代表收件人
message.setRecipient(Message.RecipientType.TO, new InternetAddress(to, "收件人名称", "utf-8"));
// To: 增加更多收件人(可选)
//message.addRecipient(MimeMessage.RecipientType.TO, new InternetAddress("chimuhuadao@126.com", "收件人名称", "UTF-8"));
//message.addRecipient(MimeMessage.RecipientType.TO, new InternetAddress("chimuhuadao@163.com", "收件人名称", "UTF-8"));
// Cc: 抄送(可选)
//message.setRecipient(MimeMessage.RecipientType.CC, new InternetAddress("chimuhuadao@126.com", "抄送人名称", "UTF-8"));
// Bcc: 密送(可选)
//message.setRecipient(MimeMessage.RecipientType.BCC, new InternetAddress("chimuhuadao@126.com", "密送人名称", "UTF-8"));
// 设置邮件主题
message.setSubject("测试转发邮件");
//====================附件测试开始=================
//以下部分为测试邮件附件,如不需要可以把整段附件这部分代码注释
// 创建 Multipart 对象,来包含邮件的多部分(正文,附件等)消息
Multipart multipart = new MimeMultipart();
// 第一部分正文消息部分
BodyPart bodyPart = new MimeBodyPart();
// 设置邮件正文内容
bodyPart.setContent("<h1>早安,世界</h1>", "text/html;charset=utf-8");
// 将正文消息部分添加到 Multipart 对象中
multipart.addBodyPart(bodyPart);
// 第二部分是附件
bodyPart = new MimeBodyPart();
// 读取项目根目录下 upload 文件夹内 00125943U-0.jpg 文件作为附件,这里路径为 D:\ApacheServer\web_java\HelloWorld\WebContent\\upload\00125943U-0.jpg。这里WebContent为实际项目运行根目录
String fileName = "00125943U-0.jpg";
String filePath = request.getServletContext().getRealPath("./") + "upload" + File.separator + fileName;
DataSource dataSource = new FileDataSource(filePath);
bodyPart.setDataHandler(new DataHandler(dataSource));
// 设置邮件中附件名称
bodyPart.setFileName("testAttachment.jpg");
// 将附件部分添加到 Multipart 对象中
multipart.addBodyPart(bodyPart);
//response.getWriter().append(filePath);if(true)return;
// 另一份附件,可发送多个附件
bodyPart = new MimeBodyPart();
// 读取项目根目录下 upload 文件夹内 00125943U-0.jpg 文件作为附件
fileName = "abc.ppt";
filePath = request.getServletContext().getRealPath("./") + "upload" + File.separator + fileName;
dataSource = new FileDataSource(filePath);
bodyPart.setDataHandler(new DataHandler(dataSource));
// 设置邮件中附件名称
bodyPart.setFileName("testAttachment.ppt");
// 将附件部分添加到 Multipart 对象中
multipart.addBodyPart(bodyPart);
//和下面发送文本的 message.setContent("<h1>早安,世界</h1>", "text/html;charset=utf-8"); 二选一执行
message.setContent(multipart);
//====================附件测试结束=================
// 设置邮件内容,可以带HTML标签,也可以不带,内容大小不限
//message.setContent("<h1>早安,世界</h1>", "text/html;charset=utf-8");
// 设置发送时间
message.setSentDate(new Date());
// 保存上面的编辑内容
message.saveChanges();
Transport transport = session.getTransport();
// 链接邮件服务器
transport.connect(from, passWord);
// 发送信息
transport.sendMessage(message, message.getAllRecipients());
// 关闭链接
transport.close();
}catch(Exception exception) {
// 处理错误
exception.printStackTrace();
}
}
// 自建方法,用来处理 GET URL 或 application/x-www-form-urlencoded 方式的 POST
protected void getOrFormUrlencoded(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
// getParameter,getParameterNames 只能获取 get 参数(POST 请求里也能用 getParameter 获取 GET 参数)或者编码格式为 application/x-www-form-urlencoded 的 post 数据,无法获取 multipart/form-data 的 post 数据
// application/x-www-form-urlencoded 传的 post 数据实际和 get 方式一样,都是格式化成 参数1=值1&参数2=值2&参数3=值3&... ,区别是 get 参数在 url 中,post 参数在请求体里。
String name_val = request.getParameter("name");
response.getWriter().append("<br/>表单名 : name 单一值为 : " + name_val);
// getParameterNames() 方法获取所有提交的表单参数名。该方法返回一个枚举对象,包含未指定顺序的参数名
Enumeration paramNames = request.getParameterNames();
// hasMoreElements() 循环遍历判断该枚举是否有更多元素,指针每次往后移一位
while(paramNames.hasMoreElements()) {
// nextElement() 每次获取该枚举一个元素,指针每次往后移一位
String paramName = (String)paramNames.nextElement();
// 根据参数名获取对应参数值,但不知道该参数名对应单个值还是多个值(复选框即是多个表单同名,不同值,与PHP不同JAVA相同复选框名后面不用加中括号 [] ),所以用数组接收
String paramValues[] = request.getParameterValues(paramName);
// 读取参数值的数据
if (paramValues.length == 1) { // 该参数名对应单一参数值
// 参数值为 paramValues[0]
String paramValue = paramValues[0];
if (paramValue.length() > 0) { // 判断该参数值有实际字符串内容
response.getWriter().append("<br/>表单名 : " + paramName + "单一值为 : " + paramValue);
}
} else {
// 读取多个值的数据
for(int i=0; i < paramValues.length; i++) {
// 值为 paramValues[i]
response.getWriter().append("<br/>多重参数名 : " + paramName + "多重id值 : "+i+"为 : " + paramValues[i]);
}
}
}
}
// 自建方法,用来处理 multipart/form-data 方式的 POST,若不想方法名后面跟 throws 抛出异常,则需在方法内执行有可能抛出异常的语句时有 try catch 捕获异常
protected void formData(HttpServletRequest request, HttpServletResponse response){
try {
// 创建磁盘文件项目工厂(form-data方式POST获取参数必须用的不管有无上传文件),可设置限制上传文件的临时存储目录等
DiskFileItemFactory factory = new DiskFileItemFactory();
// 可有可无,setSizeThreshold方法判断post数据(包括上传文件及表单数据)的大小(以字节为单位的int值,大于该值则post数据以临时文件形式存在磁盘,小于等于此值则存在内存中),如果从没有调用该方法设置此临界值,将会采用系统默认值10KB。对应的getSizeThreshold() 方法用来获取此临界值。
//factory.setSizeThreshold(1024 * 1024 * 3); // 3MB
// 可有可无,设置当上传数据大于 setSizeThreshold 设置的值时,临时文件在磁盘上的存放目录。当从没有此方法设置临时文件存储目录时,采用系统默认的临时文件路径 Tomcat 系统默认临时目录为 tomcat安装目录/temp/,默认的临时文件路径可通过 System.getProperty("java.io.tmpdir");查看
//factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
// 通过文件工厂对象创建上传对象,可设置 post 文件最大上传大小,请求数据最大值(包含文件和表单数据)等
ServletFileUpload upload = new ServletFileUpload(factory);
// 可有可无,设置上传的单个文件的最大字节数为100M
//upload.setFileSizeMax(1024 * 1024 * 100);
// 可有可无,设置整个表单的最大字节数为1G (包含文件和表单数据)
//upload.setSizeMax(1024 * 1024 * 1024);
// 可有可无,中文处理,解决上传数据的中文乱码
//upload.setHeaderEncoding("UTF-8");
// 构造路径存储上传的文件,request.getServletContext().getRealPath("./") 获取的路径 为 WEB-INF 文件夹父级目录,即项目根目录路径,这里路径为 D:\ApacheServer\web_java\HelloWorld\WebContent\\upload\00125943U-0.jpg。这里WebContent为实际项目运行根目录
String uploadPath = request.getServletContext().getRealPath("./") + "upload";
// 如果目录不存在则创建,目录同样只能一级一级创建,不能一次创建多级
File uploadDir = new File( uploadPath );
if (!uploadDir.exists()) {
uploadDir.mkdir();
}
// List类传泛型 FileItem 则其创建对象内元素都变为 FileItem 类型
// 将用户请求传给设置好参数的上传对象,返回一个数组
List<FileItem> item_list = upload.parseRequest(request);
// 创建 param_map 保存提交表单的键值对
Map param_map = new HashMap();
// 假设有未知个数同表单名的复选框表单提交信息,用 testCheckBox 存该复选框选中的值
Map testCheckBox = new HashMap();
if (item_list != null && item_list.size() > 0) {
// 遍历上传的表单元素
for(FileItem fileItem : item_list){
// fileItem.getFieldName() 表单参数名
String fieldName = fileItem.getFieldName();
// fileItem.getString("UTF-8") 获取表单参数的值,或者上传文件的文本内容
String fieldVal = fileItem.getString("UTF-8"); // 如果页面编码是 UTF-8 的
// 处理在表单中的字段
if (fileItem.isFormField()) {
response.getWriter().append("<br/>表单参数名 : " + fieldName + " 参数值为 : " + fieldVal);
// 当表单名为 testCheckBox 时,将其不确定个数的多个值存到 Map 对象中
if(fieldName.equals("testCheckBox")) {
testCheckBox.put(testCheckBox.size(), fieldVal);
}
// 把post参数以键值对形式重新存到 param_map 列表中。复选框的话最后一个选中的同名值会覆盖之前的值
param_map.put(fieldName, fieldVal);
}
// 处理表单中上传文件,这里上传文件的表单名任意都可上传
if (!fileItem.isFormField()) {
// ===============上传文件
// 获取上传的文件名(含扩展名),fileItem.getName()只能获取上传文件的完整名,不能获取其他post表单参数值
String fileName = fileItem.getName();
// 拼接将文件保存本地完整路径及文件+扩展名。File.separator 表示目录分割斜杠
String filePath = uploadPath + File.separator + fileName;
// 将合成的完整路径文件名 filePath 放入 File 对象中,File 对象代表磁盘中实际存在的文件和目录
File saveFile = new File(filePath);
// 限制上传文件扩展名
String suffix_limit[] = {".html", ".jpg", ".jsp"};
SuffixFileFilter filter = new SuffixFileFilter(suffix_limit);
boolean flag = filter.accept(saveFile);
if(!flag) {
response.getWriter().append("<br/>上传文件类型不符,文件名为 : " + fileName);
return;
}
// 将该上传文件按指定路径保存文件到硬盘
fileItem.write(saveFile);
request.setAttribute("message", "文件上传成功!");
}
}
}
// 可以使用 param_map.get 获取表单参数值了
String name_val = (String) param_map.get("name");
response.getWriter().append("<br/>表单name的值 : " + name_val);
// 遍历获取复选框的 Map 对象里每个值
for(int i = 0;i<testCheckBox.size();i++) {
response.getWriter().append("<br/>复选框i : " + i + "对应值 : " + (String) testCheckBox.get(i));
}
} catch (FileUploadException fex) {
fex.printStackTrace();
} catch (Exception ex) {
request.setAttribute("message", "错误信息: " + ex.getMessage());
}
}
}
TestFilter.java
// 文件路径 D:\ApacheServer\web_java\HelloWorld\src\com\test\TestFilter.java
package com.test;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
// 浏览器暂不能通过 http://localhost:8080/HelloWorld/TestFilter 访问页面
@WebFilter("/TestFilter")
//实现 Filter 类
public class TestFilter implements Filter {
public TestFilter() {
}
// Servlet容器在销毁 Filter 实例前调用该方法,在该方法中释放 Filter 实例占用的资源。
public void destroy() {
// TODO Auto-generated method stub
}
// 该方法完成实际的过滤操作,当客户端请求过滤器设置的 URL 时,Servlet 容器将先调用过滤器的 doFilter 方法。FilterChain 用户访问后续过滤器。
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 这里的 request 和 response 对象与 Servlet 类中一样可获取用户请求信息及直接返回响应信息
// pass the request along the filter chain
// 把请求传回过滤链
chain.doFilter(request, response);
}
// web 容器启动时,web 服务器将创建 Filter 的实例对象,并调用其 init 方法,读取 web.xml 配置,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作( filter 对象只会创建一次,init 方法也只会执行一次)。开发人员通过 init 方法的参数,可获得代表当前 filter 配置信息的 FilterConfig 对象。
public void init(FilterConfig fConfig) throws ServletException {
// 获取初始化参数
String testParam = fConfig.getInitParameter("testParam");
// 输出初始化参数
System.out.println("web.xml 配置 测试参数 testParam 值为 : " + testParam);
}
}
// Console 信息界面会出现 web.xml 配置 测试参数 testParam 值为 : 测试配置参数值 信息
TestErrorServlet.java
// 文件路径 D:\ApacheServer\web_java\HelloWorld\src\com\test\TestErrorServlet.java
package com.test;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/TestErrorServlet")
public class TestErrorServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public TestErrorServlet() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//response.getWriter().append("Served at: ").append(request.getContextPath());
// javax.servlet.error.status_code 该属性给出状态码,状态码可被存储,并在存储为 java.lang.Integer 数据类型后可被分析
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
// javax.servlet.error.request_uri 该属性给出报错页面的请求地址,可被存储,并在存储为 java.lang.String 数据类型后可被分析
String requestUri = (String) request.getAttribute("javax.servlet.error.request_uri");
// javax.servlet.error.message 该属性给出确切错误消息信息,信息可被存储,并在存储为 java.lang.String 数据类型后可被分析
String message = (String) request.getAttribute("javax.servlet.error.message");
// javax.servlet.error.servlet_name 该属性给出报错的 Servlet 类名,可被存储,并在存储为 java.lang.String 数据类型后可被分析
String servletName = (String) request.getAttribute("javax.servlet.error.servlet_name");
// javax.servlet.error.exception_type 该属性给出异常的类型,异常类型可被存储,并在存储为 java.lang.Class 数据类型后可被分析
Object exceptionType = request.getAttribute("javax.servlet.error.exception_type");
// javax.servlet.error.exception 该属性给出异常的相关信息,信息可被存储,并在存储为 java.lang.Throwable 数据类型后可被分析
Throwable exception = (Throwable) request.getAttribute("javax.servlet.error.exception");
// 设置返回响应内容类型及编码
response.setContentType("text/html;charset=UTF-8");
// 以下项是抛出异常或报 404 等错误码时均有返回信息
response.getWriter().append("<br/> 访问报错 Servlet 页面返回的错误码 : " + statusCode + " 这里显示内容为 404 或 500 ");
response.getWriter().append("<br/> 访问报错 Servlet 页面的请求地址 : " + requestUri + " 这里显示内容为 /HelloWorld/TomcatTest/TestServlet ");
response.getWriter().append("<br/> 访问报错 Servlet 页面返回的错误信息 : " + message + " 404 错误时这里显示内容为 test response status 异常错误时显示内容为 这是测试异常信息");
response.getWriter().append("<br/> 访问报错 Servlet 页面返回的报错的 Servlet 类名 : " + servletName + " 这里显示内容为 TestServlet ");
// 以下项是只有抛出异常才有值,报 404 等错误码时无返回值
response.getWriter().append("<br/> 访问报错 Servlet 页面返回的异常类型 : " + exceptionType.toString() + " 这里显示内容为 class javax.servlet.ServletException ");
response.getWriter().append("<br/> 访问报错 Servlet 页面返回的异常类型 : " + exception.getClass().getName() + " 这里显示内容为 class javax.servlet.ServletException ");
response.getWriter().append("<br/> 访问报错 Servlet 页面返回的异常信息 : " + exception.getMessage() + " 这里显示内容为 这是测试异常信息 ");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 文件路径 D:\ApacheServer\web_java\HelloWorld\WebContent\WEB-INF\web.xml -->
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- 注册一个过滤器 -->
<filter>
<!-- 注册一个过滤器名称 -->
<filter-name>TestFilter</filter-name>
<!-- 该注册名对应的实际 Filter 类的完整的包及类名 -->
<filter-class>com.test.TestFilter</filter-class>
<!-- 为过滤器指定初始化参数,它的子元素 <param-name> 指定参数的名字,<param-value> 指定参数的值 -->
<init-param>
<param-name>testParam</param-name>
<param-value>测试配置参数值</param-value>
</init-param>
</filter>
<!-- 方便测试,再注册一个 filter -->
<filter>
<filter-name>TestFilter2</filter-name>
<filter-class>com.test.TestFilter</filter-class>
<init-param>
<param-name>testParam2</param-name>
<param-value>测试配置参数值2</param-value>
</init-param>
</filter>
<!-- web.xml 中的 filter-mapping 元素的顺序决定了某个请求时 Web 容器调用 filter 的顺序 -->
<!-- <filter-mapping> 元素用于设置一个 Filter 所负责拦截的资源。一个 Filter 拦截的资源可通过两种方式来指定:资源访问的请求路径和 Servlet 名称 -->
<filter-mapping>
<!-- 设置负责此次过滤功能的 Filter 的注册名称即该值必须是在<filter>元素中声明过的过滤器的名字 -->
<filter-name>TestFilter</filter-name>
<!-- 设置该 Filter 所拦截的请求路径。此处的 /* 表示该过滤器适用于所有的 Servlet和请求路径 -->
<url-pattern>/*</url-pattern>
<!-- 也可以指定特定的 Servlet 注册名,在访问指定 Servlet 上应用该过滤器 -->
<servlet-name>TestServlet</servlet-name>
<!-- dispatcher 访问指定资源时,调用该过滤器的条件,可以是 REQUEST,,INCLUDE,,FORWARD 和 ERROR 之一,默认 REQUEST。用户可以设置一个或多个 <dispatcher> 子元素用来指定 Filter 对资源的多种调用方式进行拦截。该参数可不写 -->
<dispatcher>REQUEST</dispatcher> <!-- 当用户直接访问时,Web 容器将会调用过滤器。如果目标资源是通过 RequestDispatcher 的 include() 或 forward() 方法访问时,那么该过滤器就不会被调用 -->
<dispatcher>INCLUDE</dispatcher> <!-- 如果目标资源是通过 RequestDispatcher 的 include() 方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用 -->
<dispatcher>FORWARD</dispatcher> <!-- 如果目标资源是通过 RequestDispatcher 的 forward() 方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用 -->
<dispatcher>ERROR</dispatcher> <!-- 如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用 -->
</filter-mapping>
<!-- 如果拦截的请求相同,则先执行上一个 filter-mapping 中指定的 Filter -->
<filter-mapping>
<filter-name>TestFilter2</filter-name>
<url-pattern>/TomcatTest/TestServlet</url-pattern>
</filter-mapping>
<servlet>
<!-- Servlet 在此 xml 里的注册名 -->
<servlet-name>TestServlet</servlet-name>
<!-- 该注册名对应的实际 Servlet 类的完整包名类名 -->
<servlet-class>com.test.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<!-- 指定一个 Servlet 注册名 -->
<servlet-name>TestServlet</servlet-name>
<!-- 外部访问的网址 -->
<url-pattern>/TomcatTest/TestServlet</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>TestErrorServlet</servlet-name>
<servlet-class>com.test.TestErrorServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestErrorServlet</servlet-name>
<url-pattern>/TomcatTest/TestErrorServlet</url-pattern>
</servlet-mapping>
<!-- 当请求的路径报错时指定一个 Servlet 来处理及返回相应信息,但用户请求的 url 未重定向跳转 -->
<error-page>
<!-- 当客户端请求 web 容器返回指定错误状态代码时(示例是404,也可是403等)调用指定的 Servlet 页面 -->
<error-code>404</error-code>
<location>/TomcatTest/TestErrorServlet</location>
</error-page>
<error-page>
<!-- 当客户端请求 web 容器抛出异常时调用指定的 Servlet 页面,示例的 java.lang.Throwable 对应所有web容器抛出的异常,也可换成 javax.servlet.ServletException 或 java.io.IOException 等抛出指定异常时才调用设置的 Servlet 页面 -->
<exception-type>java.lang.Throwable</exception-type >
<location>/TomcatTest/TestErrorServlet</location>
</error-page>
<!-- 设置 session 超时时间,单位分钟,该设置将覆盖 Tomcat 默认的 30 分钟超时时间 -->
<session-config>
<session-timeout>15</session-timeout>
</session-config>
</web-app>