Servlet

Servlet介绍

概述
    Servlet是运行在服务器端的Java应用程序
    servlet程序开发动态资源的技术
    Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,
    用Java编写的服务器端程序,主要功能在于交互式地浏览和修改数据,生成动态Web内容。
作用:
    用于接收和处理http请求(接收和响应请求)
最核心的方法
    void service(ServletRequest req,ServletResponse res) 
    ServletRequest req 代表请求对象,包含请求的所有内容,用于获取请求数据
    ServletResponse res 代表响应对象,包含响应的所有内容,用于修改响应数据

Servlet开发步骤

1)java类,继承HttpServlet类
    覆盖doGet和doPost方法
2)在web应用web.xml中配置servlet
3)请求Servlet

创建Servlet

//第一种方式 继承 GenericServlet
    import java.io.IOException;
    import javax.servlet.GenericServlet;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    public class OneServlet extends GenericServlet {
        private static final long serialVersionUID = -2949013647446695209L;
        @Override
        public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        }
    }
//第二种方式 继承 HttpServlet 
    //继承自GenericServlet类,是在其基础上扩展了Http协议的Servlet
    //会先调用service方法,然后根据请求方式来进行调用doGet/doPost
    import java.io.IOException;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    public class OneServlet extends HttpServlet {
        private static final long serialVersionUID = -4435501112176421548L;
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            this.doPost(req, resp);
        }
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            super.doPost(req, resp);
        }
    }

配置Servlet

在web.xml中配置
    <servlet>
        <!-- 名称要唯一 -->
        <servlet-name>one</servlet-name>
        <!-- 全路径 -->
        <servlet-class>com.shuai.action.OneServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <!-- 要与上边的名称一致 -->
        <servlet-name>one</servlet-name>
        <!-- 访问Servlet的URL,相对于Web应用的路径 -->
        <url-pattern>/one</url-pattern>
    </servlet-mapping>

配置Servlet中设置初始化值

<servlet>
    <servlet-name>one</servlet-name>
    <servlet-class>com.shuai.action.OneServlet</servlet-class>
    <!-- 设置初始化值 -->
    <init-param>
        <param-name>initParam</param-name>
        <param-value>paramtest</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>one</servlet-name>
    <url-pattern>/one</url-pattern>
</servlet-mapping>
@Override
public void init(ServletConfig config) throws ServletException {
    //这个super一定不要删掉
    super.init(config);
    //通过ServletConfig对象读取初始化参数
    String initParameter = config.getInitParameter("initParam");
    System.out.println(initParameter);
    //获得所有参数的key
    Enumeration<String> enumeration = config.getInitParameterNames();
}
有参的init 与 无参init
    有参init:生命周期方法,必定会被服务器调用。(内部默认会调用无参的init方法)
    无参init:提供给开发者进行初始化工作的方法。

Servlet生命周期

实例化 - Servlet 容器创建 Servlet 的实例
初始化 - 容器调用 init() 方法
请求处理 - 如果请求 Servlet,则容器调用 service() 方法
服务终止/重新部署 - 销毁实例之前调用 destroy() 方法,通过stop server看

Servlet线程安全问题

Servlet 在服务器中是单实例多线程的。
    每次都会在Service方法中新建一个子线程来处理请求:new ThreadServlet()
    引起并发问题的原因:在Servlet中使用了成员变量(多个线程共享的数据)。
    测试:
        可以通过在Servlet中输出一个count值,然后线程睡眠,最后count++。
        用两个浏览器同时去访问。会发现最后输出的结果是一样的。
    解决多并发,用同步
        在doGet方法中
            synchronized (OneServlet.class) {
                //业务代码
            }
    建议:
        尽量不要在Servlet中使用成员变量
        如果使用了成员变量,那么就需要使用synchronized进行同步代码,而且尽量缩小同步的范围

Servlet的一些细节

改变Servlet的创建时机
    <servlet>
        <servlet-name>one</servlet-name>
        <servlet-class>com.shuai.action.ThreeServlet</servlet-class>
        <!-- 在项目部署启动的时候,默认创建Servlet,数值越大优先级越低 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
路径映射规则
                url-pattern    页面访问
    精确映射       /one           /one   
                   /test/one      /test/one

    模糊映射       /*             /任意路径
                   /test/*        /test/任意路径
                   *.后缀         任意路径.后缀
    注意:
        url-pattern要么以/或者*开头
        当前有多个url-pattern,优先级问题
            后缀名结尾的url-pattern优先级最低
            哪个更加精确哪个优先

ServletConfig对象

概述
    Servlet配置对象
作用
    用于读取servlet参数
如何得到ServletConfig对象
    通过有参的init方法获得
    通过getServletConfig()获得

ServletContext对象

概述
    ServletContext是servlet的上下文对象。
    代表当前web应用。
    一个web有且只有一个ServletContext对象。
获取ServletContext对象
    ServletContext servletContext = getServletConfig().getServletContext();
    ServletContext servletContext = getServletContext();
作用
    作为全局的域对象使用
    读取全局的配置参数
    转发web应用内的资源
    读取web应用内的文件
具体使用
    设置全局属性参数
        ServletContext servletContext = getServletContext();
        servletContext.setAttribute("name", "zhangsan");
        String str = (String)servletContext.getAttribute("name");
    读取全局参数
        <!-- 全局参数 -->
        <context-param>
            <param-name>onep</param-name>
            <param-value>onep</param-value>
        </context-param>
        <context-param>
            <param-name>twop</param-name>
            <param-value>twop</param-value>
        </context-param>
        ServletContext servletContext = getServletContext();
        String onep = servletContext.getInitParameter("onep");
        String twop = servletContext.getInitParameter("twop");
    转发
        ServletContext servletContext = getServletContext();
        //转发内部资源
        RequestDispatcher dispatcher = servletContext.getRequestDispatcher("info.jsp");
        dispatcher.forward(req, res);
    读取web应用内的文件
        ServletContext servletContext = getServletContext();
        //获取一个文件的绝对路径 - 获取的项目根目录
        //要以/开头
        String realPath = servletContext.getRealPath("/");
        System.out.println(realPath);
        //读取一个文件,并且返回一个文件流
        InputStream inputStream = servletContext.getResourceAsStream("/路径");

HttpServletResponse对象

response对象
    表示响应对象,包含所有的响应数据。使用response对象可以修改响应数据。
响应格式
    响应行
        http版本
        状态码 setStatus(sc)方法
        描述
    响应头
        键值对 
            setHeader(key,value)方法
            setDateHeader(key,value)方法-专门修改日期类型的响应头
            setIntHeader(key,value)方法 - 专门修改int类型的响应头
    空行
    正文
        getWriter() 修改字符类型的正文(文本,网页)minetype:text/*类型
        getOutputStream() 修改字节类型的正文(图片,视频)
getOutputStream()返回文本数据解决乱码
    系统默认是GBK
        ServletOutputStream outputStream = res.getOutputStream();
        String str = "帅哥";
        outputStream.write(str.getBytes());
    输出其它编码
        ServletOutputStream outputStream = res.getOutputStream();
        第一种方式
        仅仅在ie浏览器有效
        String meta = "<meta http-equiv='content-type' content='text/html; charset=UTF-8'>";
        //String meta = "<meta charset='UTF-8'>";
        outputStream.write(meta.getBytes());
        第二种方式
        所有浏览器有效
        response.setHeader("content-type", "text/html; charset=UTF-8");
        第三种方式
        所有浏览器有效
        response.setContentType("text/html; charset=UTF-8");
        String str = "帅哥";
        outputStream.write(str.getBytes("UTF-8"));
getWriter()返回文本数据解决乱码
    write方法默认是iso-8859-1
        只能输出字节
    输出其它编码
        修改输出字符内容的查询编码-修改的是服务器- 这个代码可以省略
            response.setCharacterEncoding("UTF-8");
        通知浏览器使用正确的编码解析
            response.setContentType("text/html; charset=UTF-8");
            PrintWriter writer = response.getWriter();
            默认编码是iso-8859-1
            writer.write("帅哥");
文件下载
    读取文件
        String path = getServletContext().getRealPath("/file/开发0710学生情况.xlsx");
        File file = new File(path);
        FileInputStream inputStream = new FileInputStream(file);
        获取输出通道
        ServletOutputStream outputStream = response.getOutputStream();
        下载提示框 响应头 content-disposition
        注意:在浏览器和服务器之间请求头和响应头内容出现中文,不能直接传输,而应该把中文内容进行URL编码才能传输
        String filename = URLEncoder.encode(file.getName(),"UTF-8");
        response.setHeader("content-disposition", "attachment;filename="+filename);
        边读边写
        byte[] buf = new byte[1024];
        int len = 0;
        while((len =inputStream.read(buf))!= -1){
            outputStream.write(buf);
        }
        关闭流
        inputStream.close();
输出随机验证码(防止恶意注册和登录)
    public void writeImage(HttpServletResponse response)throws ServletException, IOException {
        //内存生成一张图片
        int width = 120;
        int height = 50;
        BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
        //修改图片背景
        //得到画笔
        Graphics graphics = image.getGraphics();
        //绘制背景
        graphics.setColor(Color.gray);
        //绘制形状
        graphics.fillRect(0, 0, width, height);
        //设置字体
        graphics.setFont(new Font("黑体", Font.ITALIC, 30));
        //绘制数字
        graphics.setColor(Color.BLACK);
        String str = "1234";
        graphics.drawString(str, 20, 25);
        //设置随机干扰线
        Random ran = new Random();
        for(int i=1;i<=20;i++){
            int x1 = ran.nextInt(width);
            int y1 = ran.nextInt(height);
            int x2 = ran.nextInt(width);
            int y2 = ran.nextInt(height);
            //随机色
            graphics.setColor(getRanColor());
            graphics.drawLine(x1, y1, x2, y2);
        }
        //把这个图片写出给浏览器
        ServletOutputStream outputStream = response.getOutputStream();
        ImageIO.write(image, "gif", outputStream);
    }
    //获取随机颜色
    private  Color getRanColor(){
        Random ran = new Random();
        int r = ran.nextInt(256);
        int g = ran.nextInt(256);
        int b = ran.nextInt(256);
        return new Color(r,g,b);
    }
    页面使用
        ![](one)
重定向
    发出两次请求
    地址栏会发生变化
    可以重定向任何资源(包括应用外的资源)
    不可以通过request来共享数据
    代码
        response.sendRedirect("/info.jsp");

HttpServletRequest对象

概述
    表示请求对象,包含所有的请求数据。使用request获取请求的数据。
请求格式
    请求行
        请求方式  request.getMethod()
        请求资源  request.getRequestURL() / request.getRequestURI()
        http版本  request.getProtocol()
        获得get请求的参数 request.getQueryString()
    请求头
        获得一个请求头 getHeader(key) 
        获得整形的请求头 getIntHeader(key) 
        获得日期类型的请求头 getDateHeader(key) 
    空行
    正文
        获得正文 getInputStream()
防盗链
    String referer = request.getHeader("referer");
    if(referer == null || !referer.contains("/servlettest")){
        //非法连接
        return;
    }
获取表单数据
    get : getQueryString()
    post : getInputStream()
    通用方式 
        获取某个参数 getParameter("")
        获取某个参数的集合 getParameterValues("") 例如:checkbox
        获取所有的参数名 getParameterNames()
        获取所有的参数 getParameterMap()
乱码问题
    if("post".equalsIgnoreCase(request.getMethod())){
        //只能解决post提交的参数,不能解决get提交的参数,因为这个方法只能设置请求正文的内容
        request.setCharacterEncoding("UTF-8");
    }
    String name = request.getParameter("name");//默认是iso-8859-1
    if("get".equalsIgnoreCase(request.getMethod())){
        //get请求的参数需要手动解码
        name = new String(name.getBytes("iso-8859-1"),"UTF-8");
    }
转发
    发出一次请求
    地址栏不会发生变化
    只能转发应用内的资源
    可以通过request来共享数据
    代码
        //可以把数据发到转发的页面
        req.setAttribute("name", "zhangshuai");
        //第一种方式
        getServletContext().getRequestDispatcher("/info.jsp").forward(req, response);
        //第二种方式
        req.getRequestDispatcher("/info.jsp").forward(req, response);

beanutils封装参数

下载
    https://commons.apache.org/proper/commons-beanutils/
    http://commons.apache.org/proper/commons-logging/ 依赖包
导包
  commons-beanutils-1.9.3.jar
  commons-logging-1.2.jar
页面
    <form action="/easyuitest/one" method="post">
        用户名:<input type="text" name="name" /><br /> 
        密码:<input type="password" name="password" /><br /> 
        爱好: <input type="checkbox" name="hobby" value="eat" />吃 
            <input type="checkbox" name="hobby" value="sleep" />睡
            <input type="checkbox" name="hobby" value="play" />玩<br /> 
        <input type="submit" value="提交" />
    </form>
后台工具类
    public static <T> T transform(Class clazz, HttpServletRequest req) throws Exception {
        String method = req.getMethod();
        if ("post".equalsIgnoreCase(method)) {
            req.setCharacterEncoding("UTF-8");
        }
        Object instance = null;
        instance = clazz.newInstance();
        BeanUtils.populate(instance, req.getParameterMap());
        if ("get".equalsIgnoreCase(method)) {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                String name = field.getName();
                char charAt = name.charAt(0);
                String upperCase = String.valueOf(charAt).toUpperCase();
                String name2 = "set" + upperCase + name.substring(1);
                String name1 = "get" + upperCase + name.substring(1);
                Method method2 = clazz.getMethod(name2, field.getType());
                Method method3 = clazz.getMethod(name1);
                Class type = field.getType();
                Class s = String.class;
                String invoke = (String) method3.invoke(instance);
                if (s.equals(type)) {
                    invoke = new String(invoke.getBytes("iso-8859-1"), "UTF-8");
                }
                method2.invoke(instance, invoke);
            }
        }
        return (T) instance;
    }
servlet中使用
    User user = WebUtils.fillBean(request,User.class);
    System.out.println(user);
注意
    User中的属性一定要跟jsp中的属性一致。
原理
    private void datatoentity(HttpServletRequest request) {
        try {
            //约定:表单的每个控件name属性值 和 需要封装的对象的属性名称保持一致!!
            User user = new User();
            //获取所有参数
            Map<String,String[]> map = request.getParameterMap();
            for(Entry<String,String[]> entry: map.entrySet()){
                //参数名称(相当于对象的属性名)
                String paramName = entry.getKey();
                //参数值
                String[] paramValue = entry.getValue();
                //得到对象的属性(Field)
                //得到类对象
                Class clazz = user.getClass();
                //获取类的某个属性(Field)
                Field field = clazz.getDeclaredField(paramName);
                //暴力反射
                field.setAccessible(true);
                //给属性赋值
                //如果参数值有多个,则存入数组,否则存入数组的第一个元素
                if(paramValue.length>1){
                    //赋值
                    field.set(user, paramValue);
                }else{
                    field.set(user, paramValue[0]);
                }
            }
            System.out.println(user);
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

路径问题

浏览器行为   指向主机的根目录 tomcat/webapps
服务器行为   指向当前web应用的根目录  项目名称servlettest
服务端  项目根目录
    服务器行为
        getServletContext().getRealPath("/");
    服务器行为
        getServletContext().getResourceAsStream("/");
    服务器行为
        req.getRequestDispatcher("/");
    服务器行为  /是否开头都一样
        <%@ page errorPage="/" %>
    服务器行为 /是否开头都一样
        <%@ include file="/" %>
浏览器  tomcat/webapps目录
    浏览器行为
        resp.sendRedirect("/");
    浏览器行为
        /onetest/one 绝对路径,tomcat/webapps
        one 相对路径,默认是项目根目录
        <form action="/">
    浏览器行为
        /onetest/two 绝对路径,tomcat/webapps
        two 相对路径,默认是项目根目录
        <a href="/" />
    浏览器行为
        one.css 相对路径 
        /onetest/one.css 绝对路径 
        <link rel="stylesheet" type="text/css" href="/" />
    浏览器行为
        /onetest/info.js 绝对路径      
        info.js 相对路径,可以这样写,根目录就是项目 目录
        <script type="text/javascript" src="/"></script>
    浏览器行为-超链接 
        response.getWriter().write("<a href='/项目名/路径'>超链接</a>");
    浏览器行为-表单
        response.getWriter().write("<form action='/项目名/路径'></form>");

会话管理

概述
    好比是一次通话。从打开浏览器,访问多次服务器资源,关闭浏览器,这个过程就一次会话。
    是客户与Web服务器间一连串的交互过程。
    会话管理就是解决多个请求数据共享的问题。
        例如:多个用户使用浏览器与服务器进行会话的过程中,服务器怎样会每个用户保存这些数据。
两种技术
    Cookie技术:客户端技术,数据是保存在浏览器的缓存中
        定义:Cookie是一些数据,从Servlet发给浏览器,在浏览器中保存,然后通过浏览器发回给服务器。
        属性:
            name : cookie的名称
            value : cookie的数据
            comment : 注释
            path : 保存的路径
            domain : 保存的主机
            maxAge : cookie存活的时间
                负数:Cookie存活在浏览器内存,浏览器关闭Cookie丢失
                正数:Cookie存活在缓存文件中,只有删除缓存文件Cookie才会丢失。
                零  :立即过期,用于删除同名的Cookie
            version : cookie的版本
        servlet方法
            添加 addCookie()
            接收 getCookies()
        限制
            1)类型必须是字符串
            2)cookie的长度不能超过4k,一个网站1次只能最多发20个,所有网站一共发300个
            3)数据保存浏览器,安全性差
        优点
            利用浏览器资源,减轻服务器压力
    HttpSession技术:服务器端技术,数据时保存在服务器内存中的
        是一个域对象
        获取方式
            HttpSession session = request.getSession();
        常用方法
            setAttribute(key,value)
            getAttribute(key)
        特点
            增加服务器压力
            任意数据类型
            大小没有限制
            相对安全

Cookie

创建Cookie
    Cookie cookie = new Cookie("name","shuaige");
    /*
     * 设置Cookie存活时间
     * 负数:Cookie存活在浏览器内存,浏览器关闭Cookie丢失
     * 正数:Cookie存活在缓存文件中,只有删除缓存文件Cookie才会丢失。
     * 零  :立即过期,用于删除同名的Cookie
     * */
    cookie.setMaxAge(Integer.MAX_VALUE);
    //默认路径 /项目名
    cookie.setPath("/servlet/servlettest");
    //发送给浏览器
    response.addCookie(cookie);
每次浏览器请求会默认带着Cookie
    //接收Cookie
    //注意什么情况可以取出Cookie发送到服务器?  获取Cookie路径.startWith(Cookie的保存路径)=true的情况下可以获取。
    //注意:如果把Cookie的保存路径设置为当前Web应用的根目录,那么在当前web应用下所有访问资源时都能取出该Cookie
    Cookie[] cookies = request.getCookies();
    for(int i = 0;i<cookies.length;i++){
        Cookie cookie1 = cookies[i];
        System.out.println(cookie1);
    }
获取用户上次访问的时间

用Cookie来记录浏览过的商品


domain表示的是cookie所在的域,默认为请求的地址
    同域访问
        http://bbs.shuaige.com/onttest/one的默认域名是bbs.shuaige.com
    跨域访问
        http://bbs.shuaige.com/onttest/one
        http://aas.shuaige.com/onetest/one
        设置domain为.shuaige.com就可以跨域访问了。
        本域设置不可以访问的
        例如http://bbs.shuaige.com/onttest/one设置不可以访问,并且http://aas.shuaige.com/onetest/one可以访问的
        设置domain为aas.shuaige.com就可以跨域访问了。
        path是cookie所在目录
        浏览器会将domain和path都相同的cookie保存在一个文件里,cookie间用*隔开

HttpSession

存值
    //获取session
    HttpSession session = request.getSession();
    //把数据存到session中
    session.setAttribute("name", "shuaige");    
取值
    //获取session
    HttpSession session = request.getSession();
    //把数据从session中取出
    String name = (String)session.getAttribute("name");
    System.out.println(name);
request.getSession()方法
    1.查询服务器中是否存在对应的HttpSession对象
        有:返回对应的对象
        没有:创建一个新的对象返回
    2.HttpSession对象会设置一个对应浏览器的JSESSINOID,通过请求头(set-cookie)实现。
        HttpSession session = request.getSession();
        String sid = session.getId();
        服务器也是根据这个JSESSINOID来取出对应的session
    3.注意:
        request.getSession(true)/request.getSession()
            查询服务器中是否存在对应的HttpSession对象
                有:返回对应的对象
                没有:创建一个新的对象返回
        request.getSession(false)
            查询服务器中是否存在对应的HttpSession对象
                有:返回对应的对象
                没有:返回null
原理
    HttpSession对象会设置一个对应浏览器的JSESSINOID,通过请求头(set-cookie)实现。
        HttpSession session = request.getSession();
        String sid = session.getId();
        服务器也是根据这个JSESSINOID来取出对应的session
购物车

会员登录

HttpSession钝化与激活
    正常关闭服务
    HttpSession对象过久没有访问

第三方控件-上传

概述
    由非软件提供商提供的功能组件
    commons-fileupload文件上传组件
        Apache提供的实现文件上传的组件
        免费的、开源的
commons-fileupload API
    FileItemFactory接口
        用于构建FileItem实例
    DiskFileItemFactory类
        是FileItemFactory接口实现类
        FileItemFactory  factory = new DiskFileItemFactory();
    ServletFileUpload类
        组件的核心类
        封装表单元素并以集合方式返回
        ServletFileUpload(FileItemFactory  fileitemfactory)
        boolean isMultipartContent (HttpServletRequest request)
            静态方法。用于判断请求数据中的内容是否是multipart/form-data类型。是返回true,否返回false
        List parseRequest(HttpServletRequest request)
            将请求数据中的每一个字段,单独封装成FileItem对象,并以List方式返回
    FileItem类
        封装表单元素的数据
        具有对表单内容处理的方法
        常用方法
            String getFieldName() 返回表单字段元素的name属性值
            boolean isFormField() 判断FileItem封装的数据是属于普通表单字段,或者是文件表单字段。普通表单字段:true,文件表单字段:false。
            String getName() 返回上传文件字段中的文件名,文件名通常是不含路径信息的,取决于浏览器实现
            void write(File file) 将FileItem对象中的内容保存到指定文件中
            String getString() 按照默认编码格式返回字符串
            String getString(String encoding) 按照指定编码格式将内容转换成字符串返回
使用步骤
    1.获取commons-fileupload组件
        commons-fileupload-版本号.jar
        commons-io-版本号.jar
    2.导入jar包
    3.设置表单提交属性
        <form  name="form1"  method="post" enctype="multipart/form-data" />
    4.实现Servlet中的上传
        4.1判断请求数据中的内容是否是multipart/form-data类型ServletFileUpload.isMultipartContent(req)
        4.2创建FileItemFactory实例new DiskFileItemFactory()
        4.3创建ServletFileUpload实例new ServletFileUpload(factory)
        4.4解析request请求,获取表单元素对应的FileItem集合upload.parseRequest(req)
        4.5循环遍历获取数据
        4.6判断元素类型,true-普通表单元素,false-文件元素item.isFormField()
        4.7如果是文件元素
            4.7.1获取文件名称item.getName()
            4.7.2如果有名称乱码就处理乱码new String(name.getBytes("GBK"),"UTF-8")
            4.7.3写出到指定位置item.write(file)
    代码
        <form action="one" enctype="multipart/form-data" method="post">
            <input name="username" type="text"/>
            <input name="file" type="file" />
            <input type="submit" value="提交">
        </form>
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            boolean isMultipart = ServletFileUpload.isMultipartContent(req);
            if(isMultipart){
                //创建FileItemFactory实例
                //参数一:代表缓存大小。假如设置的大小是1M,如果上传的文件不超过1M,那么不用缓存,如果超过1M,就使用缓存
                //参数二:代表缓存的目录。
                //DiskFileItemFactory factory = new DiskFileItemFactory(1024*1024,new File("C:\\tempFiles"));
                FileItemFactory factory = new DiskFileItemFactory();
                //创建ServletFileUpload实例
                ServletFileUpload upload = new ServletFileUpload(factory);
                try {
                    //限制文件大小(限制单个文件不超过200KB,总文件大小不超过500KB)
                    //upload.setFileSizeMax(200*1024);//限制单个文件不超过200KB
                    //upload.setSizeMax(500*1024);//总文件大小不超过500KB
                    List<FileItem> list = upload.parseRequest(req);
                    for(FileItem item : list){
                        if(!item.isFormField()){
                            
                            //限制文件类型(只能上传图片文件)  mime类型: text/plain image/*
                            String contentType = item.getContentType();
                            if(!contentType.matches("image/[a-zA-Z]+")){
                                request.setAttribute("msg", "只允许上传图片格式的文件!");
                                request.getRequestDispatcher("/upload4.jsp").forward(request, response);
                                return;
                            }
                        
                            String name = item.getName();
                            name = new String(name.getBytes("GBK"),"UTF-8");
                            String savePath = "f:\\"+name;
                            File file = new File(savePath);
                            item.write(file);
                        }else{
                            System.out.println("表单元素:"+item.getFieldName()+"-"+item.getString());
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

分页

优势
    数据能够按照指定格式显示,布局清晰
    不受信息数量的限制
不足
    当数据量较多,页面显示不完全时,需要用户拖动页面才能浏览更多信息
实现
    Dao中
        1.获取表中数据总条数 int getCount();
        2.获取指定显示的条目 List<User> getUser(int start,int count);
    Service中
        1.每页显示条目个数
            int pagenum = 6;
        2.计算页数int getTotalPageCount()
            public int getTotalPageCount(){
                int count = getCount();
                int num = count / pagenum;
                if(count%pagenum != 0){
                    num = num+1;
                }
                return num;
            }
        3.获取指定显示的条目
            public List<User> getUsers(int pageIndex){
                int start = (pageIndex-1)*pagenum;
                List<User> user = userDao.getUser(start, pagenum);
                return user;
            }
    Controller中
        1.获取页面总条目
            String totalPageCountStr = req.getParameter("totalPageCount");
            int totalPageCount = 0;
            if(totalPageCountStr != null){
                totalPageCount = Integer.valueOf(totalPageCountStr);
            }else{
                totalPageCount = service.getTotalPageCount();
            }
        2.获取当前显示多少页
            String pageIndexStr = req.getParameter("pageIndex");
            int pageIndex = 1;
            if(pageIndexStr != null){
                pageIndex = Integer.valueOf(pageIndexStr);
            }
        3.获取需要显示的数据
            List<User> users = service.getUsers(pageIndex);
        4.设置返回参数
            req.setAttribute("pageIndex", pageIndex);
            req.setAttribute("totalPageCount", totalPageCount);
            req.setAttribute("users",users);
    jsp中
        显示数据
            <table border="1">
                <tr>
                    <th>id</th>
                    <th>name</th>
                    <th>age</th>
                </tr>
                <c:forEach items="${users}" var="user">
                    <tr>
                        <td>${user.id }</td>
                        <td>${user.name }</td>
                        <td>${user.age }</td>
                    </tr>
                </c:forEach>
            </table>
        显示所有页数的链接
            <% 
                int totalPageCount = (Integer)request.getAttribute("totalPageCount");
                int pageIndex = (Integer)request.getAttribute("pageIndex");
                for(int i = 1;i<=totalPageCount;i++){
                    StringBuffer sb = new StringBuffer();
                    if(i == pageIndex){
                        sb.append("<a style='padding: 5px;'>");
                    }else{
                        sb.append("<a href='one?pageIndex=");
                        sb.append(i);
                        sb.append("&totalPageCount=");
                        sb.append(totalPageCount);
                        sb.append("' style='padding: 5px;'>");
                    }
                    sb.append(i);
                    sb.append("</a>");
                    out.write(sb.toString());
                }
            %>

Servlet注解配置

@WebServlet("/login")

手动文件上传

//1)读取请求正文
InputStream in = request.getInputStream();
//2)转换为字符读取流
BufferedReader br = new BufferedReader(new InputStreamReader(in));
//3)第一行:读取文件的分割符
String fileTag = br.readLine();
//4)第二行:读取文件名称
String line = br.readLine();
String fileName = line.substring( line.lastIndexOf("filename=\"")+10 , line.length()-1);
//跳过两行
br.readLine();
br.readLine();
//5)读取文件内容
String str = null;
BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\targetFiles\\"+fileName));
//不断读取
while(  (str=br.readLine())!=null ){
    //忽略文件的分割符
    if( (fileTag+"--").equals(str)){
        break;
    }
    bw.write(str);//写出一行
    bw.newLine();//换行符
}
bw.close();
br.close();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,743评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,296评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,285评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,485评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,581评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,821评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,960评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,719评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,186评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,516评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,650评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,936评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,757评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,991评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,370评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,527评论 2 349

推荐阅读更多精彩内容