JavaWeb四舍五入学完了,记录下这个小项目吧。
结合上传文件和邮件发送写了一个比较综合的小项目(强烈推荐狂神说Java,本来俺以为视频教学都相对比较浅的,喜欢看书,狂神真是个宝藏up主)。
整体逻辑
整个逻辑就是在网页上获取信息(可以有jpg格式的图片,其他文件不上传),由于在同一个上下文所以利用request将信息转发到获取类,将文件转成一个字节流(初衷是不想显式的保存在本地,直接上传,事实上流本来也是缓存在某个目录下的.tmp文件,所以如果想要保存也可以自行修改代码,调用代码中的savaImage方法),再发送一封邮件给指定邮箱,由于使用的qq邮箱作为发送者,所以配置为对应的smtp主机以及由于qq邮箱使用了SSL加密,所以也需要配置一下,并使用qq邮箱分配的验证码进行发送,如果有jpg格式的图片上传就一并发过去,没有就是固定的注册成功文字信息,然后再将用户信息(除文件外的信息),利用配置JDNI数据源,用它连接mysql数据库,然后将用户信息上传到数据库中。
————————————————————————————————
具体实现
建库
CREATE TABLE users(
id int PRIMARY KEY NOT NULL AUTO_INCREMENT,
username VARCHAR(20) NOT NULL,
`password` VARCHAR(20) NOT NULL,
email VARCHAR(20) NOT NULL
)ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=0;
实体类
public class User {
private String userName;
private String password;
private String email;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public void setPassword(String password) {
this.password = password;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public String getEmail() {
return email;
}
}
DAO层
自定义的异常
public class DAOException extends Exception {
private static final long serialVersionUID = 10102L;
private String message;
public DAOException(){}
public DAOException(String message){
this.message = message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return message;
}
}
获取数据源,与Mysql数据库建立连接
public class DataSourceCache {
private static DataSourceCache instance;
private DataSource dataSource;
static {
instance = new DataSourceCache();
}
private DataSourceCache(){
Context context = null;
try {
context = new InitialContext();
//解析连接池
context = (Context) context.lookup("java:comp/env");
dataSource = (DataSource) context.lookup("jdbc/mysqll");
} catch (NamingException e) {
System.out.println("获取失败");
e.printStackTrace();
}
}
public static DataSourceCache getInstance(){
return instance;
}
public DataSource getDataSource(){
return dataSource;
}
}
DAO接口,用于规范数据库的连接
public interface DAO {
Connection getConection() throws DAOException;
}
DAO接口的基本实现类,连接Mysql数据库,获取连接和关闭资源
public class BaseDAO implements DAO {
@Override
public Connection getConection() throws DAOException {
//获取连接池资源
DataSource dataSource = DataSourceCache.getInstance().getDataSource();
try{
//如果成功获取返回连接资源
return dataSource.getConnection();
}catch (Exception e){
e.printStackTrace();
throw new DAOException();
}
}
protected void closeDBObject(ResultSet resultSet, Statement statement, Connection connection){
//关闭所有资源
if(resultSet != null){
try{
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(statement != null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
针对用户表的增和查(不过该项目中没用查功能)
public interface UserDAO extends DAO {
List<User> getProducts()throws DAOException;
void insert(User product) throws DAOException;
}
针对用户表的具体实现
public class UserDAOImpl extends BaseDAO implements UserDAO {
private static final String GET_PRODUCTS_SQL = "SELECT username,password,email FROM users";
private static final String INSERT_PRODUCT_SQL = "INSERT INTO users (username,password,email) VALUES(?,?,?)";
@Override
//查找
public List<User> getProducts() throws DAOException {
List<User> users = new ArrayList<>();
Connection connection = null;
PreparedStatement pStatement = null;
ResultSet resultSet = null;
try {
connection = getConection();
//预编译
pStatement = connection.prepareStatement(GET_PRODUCTS_SQL);
//执行
resultSet = pStatement.executeQuery();
while (resultSet.next()){
User user = new User();
user.setUserName(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
user.setEmail(resultSet.getString("email"));
users.add(user);
System.out.println(user);
}
}catch (SQLException e){
throw new DAOException("错误信息为:"+e.getMessage());
}finally {
closeDBObject(resultSet,pStatement,connection);
}
return users;
}
@Override
//插入
public void insert(User user) throws DAOException {
Connection connection = null;
PreparedStatement pStatement = null;
try {
connection = getConection();
pStatement = connection.prepareStatement(INSERT_PRODUCT_SQL);
pStatement.setString(1, user.getUserName());
pStatement.setString(2, user.getPassword());
pStatement.setString(3, user.getEmail());
pStatement.execute();
}catch (SQLException e){
throw new DAOException("插入时出错 "+e.getMessage());
}finally {
closeDBObject(null,pStatement,connection);
}
}
}
获取具体用户类的工厂
package DAO;
public class DAOFactory {
public static UserDAO getUserDAO(){
return new UserDAOImpl();
}
}
工具类
获取资源类,负责上传和获取信息
public class GetHead {
private User user = new User();
public User getUser() {
return user;
}
private HttpServletRequest req;
private String path;
private InputStream inputStream;
public GetHead(HttpServletRequest req,String path){
this.req = req;
this.path = path;
getHead();
}
public InputStream getInputStream() {
return inputStream;
}
private void getHead(){
if(!ServletFileUpload.isMultipartContent(req)){
//普通表单不处理
return;
}
try {
//创建对应文件夹
String path = creatFolder("temp");
DiskFileItemFactory factory = new DiskFileItemFactory();
//设置临时存放目录,防止存入默认系统盘
factory.setRepository(new File(path));
//设置内存缓存区,超过则写入临时文件
factory.setSizeThreshold(4096);
ServletFileUpload upload = new ServletFileUpload(factory);
//设置编码格式,防止中文乱码
upload.setHeaderEncoding("utf-8");
List<FileItem> fileItems = upload.parseRequest(req);
for (FileItem fileItem : fileItems) {
String uploadFileName = fileItem.getName();
if(uploadFileName==null){
String fled = fileItem.getFieldName();
String value = fileItem.getString("utf-8");
selectSet(fled,value);
}
if(uploadFileName==null||uploadFileName.trim().equals("")||!uploadFileName.substring(uploadFileName.lastIndexOf(".")+1).equals("jpg")){
continue;
}
inputStream = fileItem.getInputStream();
//saveImage(inputStream);
//删除临时文件
fileItem.delete();
break;
}
} catch (FileUploadException | UnsupportedEncodingException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void selectSet(String fled,String value){
if(fled.equals("username")){
req.setAttribute("username",value);
user.setUserName(value);
}else if("password".equals(fled)){
user.setPassword(value);
}else if("email".equals(fled)){
user.setEmail(value);
}
}
private String creatFolder(String path_1){
String path = this.path+"/"+path_1;
File file = new File(path);
if(!file.exists()){
file.mkdir();
}
return path;
}
private void saveImage(InputStream inputStream) throws IOException {
//如想保存下上传图片可以在上面调用该类
FileOutputStream fos = new FileOutputStream(path+"image.jpg");
byte[] buffer = new byte[1024*1024];
int len =0;
while((len=inputStream.read(buffer))>0){
fos.write(buffer,0,len);
}
fos.close();
inputStream.close();
}
}
发送邮件类,由于发送邮件需要一定的耗时,考虑用户体验继承了Thread,开一个子线程专门负责发送
public class SendEmail extends Thread {
private User user;
private InputStream inputStream;
public SendEmail(InputStream inputStream,User user){
this.inputStream = inputStream;
this.user = user;
}
public SendEmail(User user){
this.user = user;
}
@SneakyThrows
@Override
public void run() {
//创建一个配置文件
Properties prop = new Properties();
//定位到qq邮件服务器
prop.setProperty("mail.host","smtp.qq.com");
//设置邮件发送协议
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.socketFactory",sf);
//创建session保证在整个会话中有效
Session session = Session.getDefaultInstance(prop, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("邮箱地址","授权码");
}
});
//开启debug模式,打印运行状态
session.setDebug(true);
//得到连接对象,并通过用户账号与授权码连接上服务器
Transport ts = session.getTransport();
ts.connect("smtp.qq.com","邮箱地址","授权码");
//创建邮件
MimeMessage message = new MimeMessage(session);
//设置主题
message.setSubject("你好!"+user.getUserName());
//设置发送人
message.setFrom(new InternetAddress("邮箱地址"));
//TO表示主要接收人,CC表示抄送人,BCC表示秘密抄送人、
//设置收件人
message.setRecipient(Message.RecipientType.TO,
new InternetAddress(user.getEmail()));
//附件(这里是照片)
MimeBodyPart image=null;
//如需要本地图片的方式就用以下代码。而不用ByteArrayDataSource类
//FileDataSource file = new FileDataSource("E:\\javaWeb\\javaweb-01-maven02\\mail-teat\\
// target\\mail-teat\\WEB-INF\\update\\image.jpg");
if(inputStream!=null) {
//第二个参数为MIME类型,可自行百度
DataSource source = new ByteArrayDataSource(inputStream, "image/jpeg");
image = new MimeBodyPart();
DataHandler dh = new DataHandler(source);
image.setDataHandler(dh);
image.setContentID("image.jpg");
}
MimeBodyPart text = new MimeBodyPart();
text.setContent("<h1>恭喜您!<h1> <p style=\"color: pink\">"+user.getUserName()+
//如果不在此处加以标注为img,就会作为附件为bin的格式发送
"</p><br/><img src='cid:image.jpg'>" +
"<br/> 注册成功,您的密码为"
+user.getPassword()+"请妥善保管","text/html;charset=UTF-8");
MimeMultipart mm = new MimeMultipart();
mm.addBodyPart(text);
if(inputStream!=null) {
mm.addBodyPart(image);
}
//选择封装方式,有三种
mm.setSubType("related");
message.setContent(mm);
message.saveChanges();
ts.sendMessage(message,message.getAllRecipients());
ts.close();
if(inputStream!=null) {
inputStream.close();
}
}
public void setUser(User user) {
this.user = user;
}
}
控制器类
@WebServlet(urlPatterns = {"/RegisterServlet.do"})
public class RegisterServlet extends javax.servlet.http.HttpServlet {
@Override
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
//将request交给getHead工具类处理获取信息
GetHead getHead = new GetHead(request,getServletContext().getRealPath("/WEB-INF"));
User user = getHead.getUser();
SendEmail sendEmail = new SendEmail(getHead.getInputStream(),user);
//为了用户体验所以这里开了一个线程
sendEmail.start();
//将用户存入数据库
SaveUserAction.save(user);
//转发到成功页面
request.getRequestDispatcher("WEB-INF/Sucese.jsp").forward(request,response);
}
@Override
protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
}
}
jsp页面
注册页面
<%@page contentType="text/html;charset=utf-8" language="java" %>
<%@ page isELIgnored="false"%>
<html>
<head>
<title>发送邮件</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/RegisterServlet.do" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username"><br/>
密码:<input type="password" name="password"><br/>
邮箱:<input type="text" name="email"><br/>
头像(仅限jpg格式):<input type="file" name="head"><br/>
<input type="submit" value="注册">
</form>
</body>
</html>
反馈页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false"%>
<html>
<head>
<title>注册信息</title>
</head>
<body>
${username},注册邮件已发送到指定邮箱,请注意查收
</body>
</html>
总结
这个项目倒是没有什么难点,坑倒是意外的不少,上一篇文章的连接数据源那些疯狂写错的蠢蠢的坑,
然后是今天想着反之传输的也是二进制文件 (可以尝试将发送邮件中关于是图片的那行html代码注释掉,然后用本地文件的方式发送,就会发现邮箱无法识别该文件,默认识别为.bin(二进制文件)) 想着为啥不能直接发送,干嘛要本地存一遍,不过通过看Debug看inputStream的信息发现,字节流也是将这些缓存成一个tmp文件,不过俺还是不想显式的存下来所以就参考了许多博文,最后发现了代码中的方法,这个问题算是解决了,
最后又遇到了无法获取到DAO层中自写的类的问题,本地不报错,服务器上报错,那问题就可以定位到部署的文件夹下对有对应的.class文件,发现该文件夹下没有的DAO层对应的文件夹没有根据我的修改包名同步修改,rebuild了也没用,手动改后,问题解决。
github垃圾代码贡献者就是我了,不过需要自己改下配置啥的