JavaWeb

1.Servlet

1.1.Servlet原理

​ servlet是由web服务器调用的,web服务器在收到浏览器请求之后,会:

在这里插入图片描述

1.2.Mapping问题

1.一个servlet可以指定一个路径

<servlet-mapping>
  <servlet-name>Hello</servlet-name>
  <url-pattern>/hello/hello</url-pattern>
</servlet-mapping>

2.一个servlet可以指定多个映射路径

<servlet-mapping>
  <servlet-name>Hello</servlet-name>
  <url-pattern>/hello/hello1</url-pattern>
</servlet-mapping>
<servlet-mapping>
  <servlet-name>Hello</servlet-name>
  <url-pattern>/hello/hello2</url-pattern>
</servlet-mapping>
<servlet-mapping>
  <servlet-name>Hello</servlet-name>
  <url-pattern>/hello/hello3</url-pattern>
</servlet-mapping>  

3.一个servlet可以指定通用映射路径

<servlet-mapping>
  <servlet-name>Hello</servlet-name>
  <url-pattern>/hello/*</url-pattern>
</servlet-mapping>

4.默认请求路径

<servlet-mapping>
  <servlet-name>Hello</servlet-name>
  <url-pattern>/*</url-pattern>
</servlet-mapping>

5…指定一些后缀或者前缀等,*前面不能加项目映射路径

<servlet-mapping>
  <servlet-name>Hello</servlet-name>
  <url-pattern>*.lcy</url-pattern>
</servlet-mapping>

6.优先级问题

​ 指定了固有的映射路径优先级最高,如果找不到会走默认的处理请求:

<!--404-->
<servlet>
  <servlet-name>error</servlet-name>
  <servlet-class>com.lcy.servlet.ErrorServlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>error</servlet-name>
  <url-pattern>/*</url-pattern>
</servlet-mapping>

1.3.ServletContext

web容器在启动的时候,会为每个web程序创建一个对应的ServletContext对象,它代表了当前的web应用。

1.共享数据

我们在这个Servlet中保存的数据,可以在另外一个servlet中拿到。

在这里插入图片描述

public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        this.getInitParameter();  初始化参数
//        this.getServletConfig();  Servlet配置
//        this.getServletContext(); Servlet上下文
        ServletContext context = this.getServletContext();
        String name ="平安";  //数据
        context.setAttribute("username",name); //将一个数据保存在ServletContext中  键值
    }
}
public class GetServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext c = this.getServletContext();
        String username = (String) c.getAttribute("username");
        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
        resp.getWriter().print("名字:"+username);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}
<!--注册servlet -->
<servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.lcy.HelloServlet</servlet-class>
</servlet>
<!--servlet请求路径-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

<!--注册servlet -->
<servlet>
    <servlet-name>get</servlet-name>
    <servlet-class>com.lcy.GetServlet</servlet-class>
</servlet>
<!--servlet请求路径-->
<servlet-mapping>
    <servlet-name>get</servlet-name>
    <url-pattern>/get</url-pattern>
</servlet-mapping>
2.获取初始化参数
<!--配置web应用初始化参数-->
<context-param>
    <param-name>url</param-name>
    <param-value>jdbc:mysql://localhost:3306/db_spring</param-value>
</context-param>
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    ServletContext context = this.getServletContext();
    String url = context.getInitParameter("url");
    resp.setCharacterEncoding("utf-8");
    resp.setContentType("text/html");
    resp.getWriter().print(url);

}
3.请求转发
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    ServletContext context = this.getServletContext();
    System.out.println("进入了demo04");
    RequestDispatcher requestDispatcher = context.getRequestDispatcher("/demo"); //转发的请求路径
    requestDispatcher.forward(req,resp);  //调用forward实现请求转发

}

在这里插入图片描述

4.读取资源文件

Properties

​ 1.在Java目录下创建properties

​ 2.在resources目录下创建properties

发现:被打包到了同一个路径下:classes,俗称:classpath

思路:需要一个文件流

username=root
pwd=root
public class ServletDemo05 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
        Properties prop = new Properties();
        prop.load(is);
        String name = prop.getProperty("username");
        String pwd = prop.getProperty("pwd");
        resp.getWriter().print(name+":"+pwd);
    }

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

访问测试即可;

1.4.HttpServletResponse

web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象,代表响应的HttpServletResponse:

  • 如果要获取客户端请求过来的参数:找HttpServletRequest

  • 给客户端响应一些信息:找HttpServletResponse

    1.简单分类

    负责向浏览器发送数据的方法

ServletOutputStream getOutputStream() throws IOException;

PrintWriter getWriter() throws IOException;

​ 负责向浏览器发送响应头的方法

	void setCharacterEncoding(String var1);

	void setContentLength(int var1);

	void setContentLengthLong(long var1);

	void setContentType(String var1);

    void setDateHeader(String var1, long var2);

    void addDateHeader(String var1, long var2);

    void setHeader(String var1, String var2);

    void addHeader(String var1, String var2);

    void setIntHeader(String var1, int var2);

    void addIntHeader(String var1, int var2);

​ 响应状态码

	int SC_OK = 200;   //请求响应成功
	int SC_MULTIPLE_CHOICES = 300;	//请求重定向
	int SC_NOT_FOUND = 404;	//找不到资源
	int SC_INTERNAL_SERVER_ERROR = 500;	//网关错误
2.下载文件

​ 1.向浏览器输出信息

​ 2.下载文件

​ 1.获取下载文件的路径

​ 2.下载的文件名是啥?

​ 3.想办法让浏览器支持下载我们需要的东西

​ 4.获取下载文件的输入流

​ 5.创建缓冲区

​ 6.获取OutputStream对象

​ 7.将FileOutputStream流写入到buffer缓冲区中

​ 8.使用OutputStream将缓冲区的数据输出到客户端!

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //1.获取下载文件的路径
    String realPath = "E:\\java-web\\response\\target\\classes\\1.下载.jpg";
    System.out.println("路径:"+realPath);
    //2.下载的文件名是啥
    String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
    //3.设置想办法让浏览器能够支持下载我们需要的东西,中文文件名URLEncoder.encode编码
    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);
    }
    in.close();
    out.close();
}
3.验证码功能

​ 验证码怎么来的?

  • 前端实现

  • 后端实现,需要用到Java的图片类,生产一个图片

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //让浏览器2秒自动刷新一次
        resp.setHeader("refresh","2");
        //在内存中创建一个图片
        BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);
        //得到图片
        Graphics2D g = (Graphics2D) image.getGraphics();
        //设置图片的背景颜色
        g.setColor(Color.white);
        g.fillRect(0,0,80,20);
        //给图片写数据
        g.setColor(Color.blue);
        g.setFont(new Font(null,Font.BOLD,20));
        g.drawString(makeNum(),0,20);
        //告诉浏览器这个请求用图片的方式打开
        resp.setContentType("image/png");
        //网站存在缓存:不然浏览器缓存
        resp.setDateHeader("expires",-1);
        resp.setHeader("Cache-Control","no-cache");
        resp.setHeader("Pragma","no-cache");
        //把图片写给浏览器
        ImageIO.write(image,"jpg",resp.getOutputStream());
    }
    
    //生成随机数
    private String makeNum() {
        Random random = new Random();
        String num = random.nextInt(9999999)+"";
        StringBuffer sb = new StringBuffer();
        for (int j = 0; j < 7-num.length(); j++) {
            sb.append("0");
        }
        num = sb.toString()+num;
        return num;
    }
    
    4.实现重定向

在这里插入图片描述

B一个web资源收到客户端请求,B会通知A客户端去访问另外一个web资源,这个过程重定向

​	常见场景:

   * 用户登陆

     ```Java
     void sendRedirect(String var1) throws IOException;
     ```

​ 测试:

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

    /**
     *resp.setHeader("Location","/response/img");
     * resp.setStatus(302);
     */
    resp.sendRedirect("/response/img");  //重定向
}
面试题:重定向和转发的区别

​ 相同点:

		* 页面会实现跳转

​ 不同点:

  • 请求转发时,url不会发生变化

  • 重定向的时候,url地址栏会发生变化

    测试:重定向
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //处理请求
        String name = req.getParameter("username");
        String pwd = req.getParameter("password");
        System.out.println(name+":"+pwd);
        //重定向的时候一定要注意路径问题,否则404
        resp.sendRedirect("/response/index2.jsp");
    }
    
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    <h1>登陆成功</h1>
    </body>
    </html>
    
    <html>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <body>
    <h2>Hello World!</h2>
    <%--这里提交的路径,需要寻找到项目的路径--%>
    <%--${pageContext.request.contextPath} 代表当前的项目--%>
    <form action="${pageContext.request.contextPath}/login" method="get">
        用户名:<input type="text" name="username">
        密码:<input type="password" name="password">
        <input type="submit">
    </form>
    </body>
    </html>
    

1.5.HttpServletRequest

HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,Http请求中的所有信息会被封装到HttpServletRequest,通过这个HttpServletRequest的方法,获得客户端的所有信息;

1.获取前端传递的参数,并且请求转发
	String getParameter(String var1);

    Enumeration<String> getParameterNames();   //不常用

    String[] getParameterValues(String var1);   

    Map<String, String[]> getParameterMap();	//不常用
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    req.setCharacterEncoding("utf-8");
    resp.setCharacterEncoding("utf-8");
    String username = req.getParameter("username");
    String password = req.getParameter("password");
    String[] hobbies = req.getParameterValues("hobby");
    System.out.println("=========================================");
    System.out.println(username);
    System.out.println(password);
    System.out.println(Arrays.toString(hobbies));
    System.out.println("============================================");

    System.out.println(req.getContextPath());
    //通过请求转发
    //这里的/代表当前的web项目
    req.getRequestDispatcher(req.getContextPath()+"/login.jsp").forward(req,resp);

}

重定向和转发的区别:

相同点:

* 页面都会实现跳转

不同点:

  • 请求转发的时候,url不会产生变化,307

  • 重定向的时候,url地址栏会发生变化,302

2.session cookie

2.1 会话

​ **会话:**用户打开一个浏览器,点击了很多超链接,访问多个web资源,关闭浏览器。这个过程为 会话

​ **有状态会话:**一个同学来过教室,下次再来的时候,我们会知道这个同学曾经来过

例子:你怎么证明你是清华的学生?

​ 你 清华

​ 1.发票 清华给你的发票

​ 2.学校登记 清华标记你来过了

一个网站证明证明你来过?

​ 客户端 服务端

​ 1.服务端给客户端一个信件,客户端下次访问的时候带上信件就ok; cookie

​ 2.服务器登记你来过了,下次你来的时候我匹配你; session

2.2 保存会话的两种技术

cookie

​ 客户端技术(响应,请求)

session

​ 服务器技术,利用这个技术,可以保存用户的会话信息?我们可以把信息或者数据放在session中。

常见应用:网站登陆后,下次不用再登陆了。第二次直接上去了。

2.3 Cookie

在这里插入图片描述

Cookie[] cookies = req.getCookies();  //从请求中获取cookie
cookie.getName();	//获取cookie的key
cookie.getValue();	//获取cookie的值
cookie.setMaxAge(24*60*60);	//设置cookie的有效期
Cookie cookie = new Cookie("time",System.currentTimeMillis()+"");//创建一cookie
resp.addCookie(cookie);  //响应cookie给客户端
删除Cookie

​ 不设置有效期,关闭浏览器,自动失效

​ 设置有效期时间为0

编码解码
URLEncoder.encode("平安","utf-8");
URLDecoder.decode(cookie.getValue(), "utf-8");

2.4 Session(重点)

在这里插入图片描述

session和coolie的区别
  • Cookie是把用户的数据写给用户的浏览器,浏览器保存(客户端可以保存多个)

  • Session把用户的数据写到用户独占的Session中,在服务器端保存(保存重要的东西,减少服务器的资源)

  • Session对象由服务器创建

    使用场景:

    • 保存一个登陆用户的信息
    • 购物车信息
    • 在整个网站中,经常使用的数据,保存在session中;(不用去new新的对象,取就可以了,方便)
使用session
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //解决乱码
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        resp.setContentType("text/html");
        //得到session
        HttpSession session = req.getSession();
        //给session中存东西
        session.setAttribute("name", new Stu(3,"陆沉"));
        //获取session的Id
        String id = session.getId();
        //判断是不是新创建的session
        if (session.isNew()){
            resp.getWriter().write("session创建成功,Id为"+id);
        }else {
            resp.getWriter().write("已经存在,id"+id);
        }
        //session在创建的时候做了什么事情
//        Cookie jsessionid = new Cookie("JSESSIONID", id);
//        resp.addCookie(jsessionid);
    }
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //解决乱码
    req.setCharacterEncoding("utf-8");
    resp.setCharacterEncoding("utf-8");
    resp.setContentType("text/html");
    //得到session
    HttpSession session = req.getSession();
    //获取
    Stu name = (Stu) session.getAttribute("name");
    System.out.println(name);
    resp.getWriter().write(String.valueOf(name));
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //解决乱码
    req.setCharacterEncoding("utf-8");
    resp.setCharacterEncoding("utf-8");
    resp.setContentType("text/html");
    //得到session
    HttpSession session = req.getSession();
    //移除name
    session.removeAttribute("name");
    //手动注销session
    session.invalidate();
}

会话自动过期: web.xml配置

<!--设置session的默认失效时间-->
<session-config>
    <!--分钟为单位,自动失效-->
    <session-timeout>1440</session-timeout>
</session-config>

3.JSP

3.1 什么是jsp

Java Server Page:Java服务器页面,和servlet一样,用于动态web技术

最大的特点:
  • 写jsp就像在写HTML

  • 区别

    • HTML只给用户提供静态的数据

    • JSP页面可以嵌入Java代码,为用户提供动态数据

3.2 JSP原理

思路:JSP到底怎么执行的:

在这里插入图片描述
在这里插入图片描述

浏览器向服务器发送请求,不管访问什么资源,其实都是在访问servlet!

jsp最后也会被转换为Java类

JSP本质上就是一个Servlet
  //初始化
public void _jspInit() {
  
}
//销毁
public void _jspDestroy() {
    
}
//JSPService
public void _jspService(HttpServletRequest request,HttpServletResponse response)

_jspService干了什么

1.判断是什么请求

2.内置一些对象

final javax.servlet.jsp.PageContext pageContext; //页面上下文
javax.servlet.http.HttpSession session = null;	//session
final javax.servlet.ServletContext application;	//application
final javax.servlet.ServletConfig config;	//config
javax.servlet.jsp.JspWriter out = null;	//out
final java.lang.Object page = this;		//page
HttpServletRequest request		//请求
HttpServletResponse response	//响应

3.输出页面前增加的代码

  response.setContentType("text/html");  //设置响应的页面类型
  pageContext = _jspxFactory.getPageContext(this, request, response,
  			null, true, 8192, true);
  _jspx_page_context = pageContext;
  application = pageContext.getServletContext();
  config = pageContext.getServletConfig();
  session = pageContext.getSession();
  out = pageContext.getOut();
  _jspx_out = out;

4.以上这些对象我们可以直接在JSP页面中使用

jsp执行过程

在这里插入图片描述

在jsp页面中:

只要是Java代码就会原封不动的输出;

 out.print(name);

HTML代码就会被转换为以下格式输出到前端

out.write("<html>\r\n");

3.3 JSP基础语法

JSP声明会被编译到_jspService方法之外,其他的被生成到_jspService方法中

<%=变量或者表达式%>   //原来将程序的输出,输出到客户端
<%Java代码%> 	//jsp脚本片段
<%! %>	//jsp声明

JSP指令:破解语法

<jsp:include page="work.jsp"/>  //更灵活
<%@include file="index.jsp"%>

3.4 九大内置对象

  • PageContext 存东西

  • Request 存东西

  • Response 响应

  • Session 存东西

  • Application (ServletContext)存东西

  • Config (ServletConfig)

  • out

  • page

  • exception

    <!--四个可以存储东西的内置对象 -->
        //存
    pageContext.setAttribute("name1","陈平安1",3);  //保存的数据只在一个页面有效
    request.setAttribute("name2","陈平安2");   //保存的数据只在一次请求中有效,请求转发会携带资格数据
    session.setAttribute("name3","陈平安3");   //保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
    application.setAttribute("name4","陈平安4");   //保存的数据值在服务器中有效,从打开服务器到关闭服务器
    
    

request:客户端向服务器,产生的数据,用户看完就没用了,比如:新闻,用户看完没用的

session:客户端向服务端发送请求,产生的数据,用户用完一会还会用,比如:购物车

application:客户端向服务端发送请求,产生的数据,一个用户用完了,其他用户可能还会使用,比 如聊天数据

3.5 Filter过滤器

Filter:过滤器,用来过滤网站的数据

  • 处理中文乱码
  • 登陆验证…

在这里插入图片描述

过滤器的使用

  • 导包

    servlet下的Filter包

  • 编写过滤器

@Override
//初始化,和web服务器一起启动,随时等待过滤器对象出现
public void init(FilterConfig filterConfig) throws ServletException {
    System.out.println("已经初始化");
}

@Override
/**
 * chain
 * 1.过滤中的所有代码,在过滤特定请求的时候都会执行
 * 2.必须要让过滤器继续执行  chain.doFilter(req,resp);
 */
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
    req.setCharacterEncoding("utf-8");
    resp.setCharacterEncoding("utf-8");
    resp.setContentType("text/html;charset=utf-8");
    System.out.println("执行前");
    chain.doFilter(req,resp);   //如果不写,程序到此拦截停止
    System.out.println("执行后");
}

@Override
//web服务器关闭时一同销毁
public void destroy() {
    System.out.println("已经销毁");
}
  • 在web.xml中配置Filter
<servlet>
    <servlet-name>show</servlet-name>
    <servlet-class>servlet.ShowServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>show</servlet-name>
    <url-pattern>/servlet/show</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>show</servlet-name>
    <url-pattern>/show</url-pattern>
</servlet-mapping>

<filter>
    <filter-name>filter1</filter-name>
    <filter-class>filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>filter1</filter-name>
    <!--只要是/servlet的任何请求,会经过这个过滤器 -->
    <url-pattern>/servlet/*</url-pattern>
    <!--<url-pattern>/*</url-pattern>全过滤-->
</filter-mapping>
登陆拦截实现
  • 用户登陆之后,向Session中放入用户的数据
  • 进入主页的时候判断用户是否已经登陆
  • 登陆失败或者输入失败
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //获取前端请求的参数
    String username = req.getParameter("username");
    if (username.equals("admin")){
        req.getSession().setAttribute(Constant.USER_SESSION,req.getSession().getId());
        resp.sendRedirect("/sys/result.jsp");
    }else {
        resp.sendRedirect("/error.jsp");
    }
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    //转换成HttpServletRequest HttpServletResponse
    HttpServletRequest request =  (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse) servletResponse;
    if (request.getSession().getAttribute(Constant.USER_SESSION) == null) {
        response.sendRedirect("/error.jsp");
    }
    filterChain.doFilter(servletRequest, servletResponse);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    Object user_session = req.getSession().getAttribute(Constant.USER_SESSION);
    if (user_session != null) {
        req.getSession().removeAttribute(Constant.USER_SESSION);
        resp.sendRedirect("/login.jsp");
    }else {
        resp.sendRedirect("/login.jsp");
    }
}

3.3 JDBC事务

要么都成功,要么都失败

ACID原则:保证数据的安全

  • 开启事务

  • 事务提交

  • 事务回滚

  • 关闭事务

4.文件上传

4.1 文件上传的调优

  • 保证服务器安全,上传文件放在外界无法直接访问的目录下,WEB-INF
  • 防止文件覆盖,要为上传文件产生一个唯一的文件名(时间戳,-uuid)
  • 限制上传文件的最大值
  • 限制上传文件的类型,收到上传文件名,判断后缀名是否合法

代码:

servlet代码

package com.lcy.servlet;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

/**
 * @ClassName: UploadHandleServlet
 * @Description: TODO(这里用一句话描述这个类的作用)
 * @author: 孤傲苍狼
 * @date: 2015-1-3 下午11:35:50
 *
 */
public class UploadHandleServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //得到上传文件的保存目录,将上传的文件存放于WEB-INF目录下,不允许外界直接访问,保证上传文件的安全
        String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
        //上传时生成的临时文件保存目录
        String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp");
        File tmpFile = new File(tempPath);
        if (!tmpFile.exists()) {
            //创建临时目录
            tmpFile.mkdir();
        }

        //消息提示
        String message = "";
        try{
            //使用Apache文件上传组件处理文件上传步骤:
            //1、创建一个DiskFileItemFactory工厂
            DiskFileItemFactory factory = new DiskFileItemFactory();
            //设置工厂的缓冲区的大小,当上传的文件大小超过缓冲区的大小时,就会生成一个临时文件存放到指定的临时目录当中。
            factory.setSizeThreshold(1024*100);//设置缓冲区的大小为100KB,如果不指定,那么缓冲区的大小默认是10KB
            //设置上传时生成的临时文件的保存目录
            factory.setRepository(tmpFile);
            //2、创建一个文件上传解析器
            ServletFileUpload upload = new ServletFileUpload(factory);
            //监听文件上传进度
            upload.setProgressListener(new ProgressListener(){
                public void update(long pBytesRead, long pContentLength, int arg2) {
                    System.out.println("文件大小为:" + pContentLength + ",当前已处理:" + pBytesRead);
                    /**
                     * 文件大小为:14608,当前已处理:4096
                     文件大小为:14608,当前已处理:7367
                     文件大小为:14608,当前已处理:11419
                     文件大小为:14608,当前已处理:14608
                     */
                }
            });
            //解决上传文件名的中文乱码
            upload.setHeaderEncoding("UTF-8");
            //3、判断提交上来的数据是否是上传表单的数据
            if(!ServletFileUpload.isMultipartContent(request)){
                //按照传统方式获取数据
                return;
            }

            //设置上传单个文件的大小的最大值,目前是设置为1024*1024字节,也就是1MB
            upload.setFileSizeMax(1024*1024*10);
            //设置上传文件总量的最大值,最大值=同时上传的多个文件的大小的最大值的和,目前设置为10MB
            upload.setSizeMax(1024*1024*10);
            //4、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每一个FileItem对应一个Form表单的输入项
            List<FileItem> list = upload.parseRequest(request);
            for(FileItem item : list){
                //如果fileitem中封装的是普通输入项的数据
                if(item.isFormField()){
                    String name = item.getFieldName();
                    //解决普通输入项的数据的中文乱码问题
                    String value = item.getString("UTF-8");
                    //value = new String(value.getBytes("iso8859-1"),"UTF-8");
                    System.out.println(name + "=" + value);
                }else{//如果fileitem中封装的是上传文件
                    //得到上传的文件名称,
                    String filename = item.getName();
                    System.out.println(filename);
                    if(filename==null || filename.trim().equals("")){
                        continue;
                    }
                    //注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如:  c:\a\b\1.txt,而有些只是单纯的文件名,如:1.txt
                    //处理获取到的上传文件的文件名的路径部分,只保留文件名部分
                    filename = filename.substring(filename.lastIndexOf("\\")+1);
                    //得到上传文件的扩展名
                    String fileExtName = filename.substring(filename.lastIndexOf(".")+1);
                    //如果需要限制上传的文件类型,那么可以通过文件的扩展名来判断上传的文件类型是否合法
                    System.out.println("上传的文件的扩展名是:"+fileExtName);
                    //获取item中的上传文件的输入流
                    InputStream in = item.getInputStream();
                    //得到文件保存的名称
                    String saveFilename = makeFileName(filename);
                    //得到文件的保存目录
                    String realSavePath = makePath(saveFilename, savePath);
                    //创建一个文件输出流
                    FileOutputStream out = new FileOutputStream(realSavePath + "\\" + saveFilename);
                    //创建一个缓冲区
                    byte buffer[] = new byte[1024];
                    //判断输入流中的数据是否已经读完的标识
                    int len = 0;
                    //循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据
                    while((len=in.read(buffer))>0){
                        //使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "\\" + filename)当中
                        out.write(buffer, 0, len);
                    }
                    //关闭输入流
                    in.close();
                    //关闭输出流
                    out.close();
                    //删除处理文件上传时生成的临时文件
                    //item.delete();
                    message = "文件上传成功!";
                }
            }
        }catch (FileUploadBase.FileSizeLimitExceededException e) {
            e.printStackTrace();
            request.setAttribute("message", "单个文件超出最大值!!!");
            request.getRequestDispatcher("/message.jsp").forward(request, response);
            return;
        }catch (FileUploadBase.SizeLimitExceededException e) {
            e.printStackTrace();
            request.setAttribute("message", "上传文件的总的大小超出限制的最大值!!!");
            request.getRequestDispatcher("/message.jsp").forward(request, response);
            return;
        }catch (Exception e) {
            message= "文件上传失败!";
            e.printStackTrace();
        }
        request.setAttribute("message",message);
        request.getRequestDispatcher("/message.jsp").forward(request, response);
    }

    /**
     * @Method: makeFileName
     * @Description: 生成上传文件的文件名,文件名以:uuid+"_"+文件的原始名称
     * @Anthor:孤傲苍狼
     * @param filename 文件的原始名称
     * @return uuid+"_"+文件的原始名称
     */
    private String makeFileName(String filename){  //2.jpg
        //为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名
        return UUID.randomUUID().toString() + "_" + filename;
    }

    /**
     * 为防止一个目录下面出现太多文件,要使用hash算法打散存储
     * @Method: makePath
     * @Description:
     * @Anthor:孤傲苍狼
     *
     * @param filename 文件名,要根据文件名生成存储目录
     * @param savePath 文件存储路径
     * @return 新的存储目录
     */
    private String makePath(String filename,String savePath){
        //得到文件名的hashCode的值,得到的就是filename这个字符串对象在内存中的地址
        int hashcode = filename.hashCode();
        int dir1 = hashcode&0xf;  //0--15
        int dir2 = (hashcode&0xf0)>>4;  //0-15
        //构造新的保存目录
        String dir = savePath + "\\" + dir1 + "\\" + dir2;  //upload\2\3  upload\3\5
        //File既可以代表文件也可以代表目录
        File file = new File(dir);
        //如果目录不存在
        if(!file.exists()){
            //创建目录
            file.mkdirs();
        }
        return dir;
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        doGet(request, response);
    }
}

jsp页面

<form action="${pageContext.request.contextPath}/servlet/UploadHandleServlet" enctype="multipart/form-data" method="post">
    上传用户:<input type="text" name="username"><br/>
    上传文件1:<input type="file" name="file1"><br/>
    上传文件2:<input type="file" name="file2"><br/>
    <input type="submit" value="提交">
</form>

web.xml配置

<servlet>
    <servlet-name>UploadHandleServlet</servlet-name>
    <servlet-class>com.lcy.servlet.UploadHandleServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>UploadHandleServlet</servlet-name>
    <url-pattern>/servlet/UploadHandleServlet</url-pattern>
</servlet-mapping>
javaWeb发送邮件

servlet

package com.lcy.servlet;

import com.lcy.bean.User;
import com.lcy.util.SendEmail;

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 {
        //接收用户请求,封装成对象
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String email = req.getParameter("email");
        System.out.println(email);
        User user = new User(username, password, email);

        //用户注册成功之后,给用户发送一封邮件
        //我们使用线程来专门发送邮件,防止出现耗时和网站注册人数过多的情况
        SendEmail send = new SendEmail(user);
        //启动线程,线程启动之后会执行run方法来发送邮件
        send.start();

        //注册用户
        req.setAttribute("message","注册成功,请注意查收邮件");
        req.getRequestDispatcher("info.jsp").forward(req,resp);

    }

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

util封装邮件发送异步处理方法

package com.lcy.util;

import com.lcy.bean.User;
import com.sun.mail.util.MailSSLSocketFactory;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import java.util.Properties;
import java.util.UUID;

//网站三秒原则:用户体验
//多线程实现用户体验   异步处理
public class SendEmail extends Thread {

    //用于给用户发送邮件的邮箱
    private String from = "1151788973@qq.com";
    //邮箱的用户
    private String username = "1151788973@qq.com";
    //邮箱的密码
    private String password = "utsgwcbpugougfbh";
    //发送邮件的服务器地址
    private String host = "smtp.qq.com";

    private User user;
    public  SendEmail(User user) {
        this.user = user;
    }

    @Override
    public void run() {
        try {
            Properties prop = new Properties();
            prop.setProperty("mail.host", host);  //设置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);

            //1.创建定义整个应用程序所需要的环境信息的session对象
            Session session = Session.getDefaultInstance(prop, new Authenticator() {
                public PasswordAuthentication getPasswordAuthentication() {
                    //发件人邮件用户名,授权码
                    return new PasswordAuthentication("1151788973@qq.com", "utsgwcbpugougfbh");
                }
            });

            //开启session的debug模式,可以看到email发送的运行状态
            session.setDebug(true);

            //2.通过session得到transport对象
            Transport ts = session.getTransport();

            //3.使用邮箱的用户名和授权码连上邮件服务器
            ts.connect(host, username, password);

            //4.创建邮件
            MimeMessage message = new MimeMessage(session);
            //4.1指明邮件的发件人
            message.setFrom(new InternetAddress(from));
            //4.2指明收件人
            message.setRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));
            //4.3邮件主题
            message.setSubject("用户注册");

            //5.邮件内容
            String text = "恭喜您注册成功,您的用户名:"+user.getUsername()+"您的密码:"+user.getPassword()+".请妥善保管";

            message.setContent(text,"text/html;charset=UTF-8");
            message.saveChanges();

            //6.发送邮件
            ts.sendMessage(message, message.getAllRecipients());
            //7.关闭连接
            ts.close();

        }catch (Exception e){
            throw  new RuntimeException(e);
        }

    }
}

web.xml

<servlet>
    <servlet-name>RegisterServlet</servlet-name>
    <servlet-class>com.lcy.servlet.RegisterServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>RegisterServlet</servlet-name>
    <url-pattern>/RegisterServlet.do</url-pattern>
</servlet-mapping>

实体类

private String username;
private String password;
private String email;

jsp页面

<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>
       //4.2指明收件人
            message.setRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));
            //4.3邮件主题
            message.setSubject("用户注册");

            //5.邮件内容
            String text = "恭喜您注册成功,您的用户名:"+user.getUsername()+"您的密码:"+user.getPassword()+".请妥善保管";

            message.setContent(text,"text/html;charset=UTF-8");
            message.saveChanges();

            //6.发送邮件
            ts.sendMessage(message, message.getAllRecipients());
            //7.关闭连接
            ts.close();

        }catch (Exception e){
            throw  new RuntimeException(e);
        }

    }
}

web.xml

<servlet>
    <servlet-name>RegisterServlet</servlet-name>
    <servlet-class>com.lcy.servlet.RegisterServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>RegisterServlet</servlet-name>
    <url-pattern>/RegisterServlet.do</url-pattern>
</servlet-mapping>

实体类

private String username;
private String password;
private String email;

jsp页面

<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>
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐