Servlet实践--留言板-v1

功能介绍:

  由三个jsp页面组成,在doGet中根据请求URL中的请求参数不同,跳转到不同的页面:

    页面1:显示整个留言板列表

    页面2:创建留言页面(包括用户、主题、内容和上传文件)

    页面3:在查看单个留言的详细内容(包括提供下载附件)

  在doPost中处理创建留言的逻辑

如何实现这些功能:

  1.使用什么来保存用户创建的留言(数据存储):

     使用一个Ticket类对象来保存用户创建的留言,包括用户名、评论主题、评论内容和附件。附件是用一个Attachment类的实例来表示,该类中包含附件名和附件内容的二进制表示。

     当需要查看某个留言详细信息的时候,可以通过一个存储在内存中的哈希map(ticketDatabase)(以一个唯一标识某个留言的id为键,以Ticket类对象为值)来找到该Ticket类对象,通过该对象就可以得到详细信息。

     当需要浏览整个留言列表时,可以通过遍历该ticketDatabase得到整个留言列表。

     当需要下载某个留言中的附件时,可以先通过该ticketDatabase找到该Ticket类对象,而在Ticket类对象中也同样保存着一个哈希map(attachments)(以附件名为键,以Attachment类对象为值),通过该哈希map就可以找到某个特定的附件。

  2.如何让servlet处理不同的请求(逻辑处理):

     根据请求URL中的请求参数不同,让处理请求的doGet()和doPost()方法调用不同功能的方法实现。例如:

        当不带任何请求参数的请求URL,默认其行为为浏览整个留言列表(action=list);

        当携带请求参数时,若action=create并以Get方式提交请求,则执行将请求和响应转发到ticketForm.jsp页面;

                 若action=view并以Get方式提交请求,则先从请求参数中获得ticketId(唯一标识某个特定的Ticket对象),然后通过ticketDatabase获得该ticketId对应的Ticket对象,把该ticketId和Ticket对象保存在请求中,然后转发到viewTicket.jsp页面

        当携带请求参数时,若action=download并以Get方式提交请求,则执行下载附件的相关操作:

            +先获得该ticketId和Ticket对象,然后通过请求参数中的attachment获得附件名,得到Ticket对象和附件名,就可以得到Attachment对象

            +通过Attachment对象,就可以得到附件内容的二进制表示(一个二进制数组)

            +强制浏览器询问用户是保存还是下载文件,设置附件的内容类型是通用的二进制内容类型

            +将附件内容的二进制数组写入ServletOutputStream输出流中

       当携带请求参数时,若action=list并以Get方式提交请求时,将ticketDatabase保存在请求中,然后转发到listTickets.jsp页面;

       当携带请求参数时,若action=createt并以Post方式提交请求时,将从表单中获取Ticket对象的相关属性(用户名、留言主题、留言内容、上传的文件),通过setXXX()方法分别设置Ticket对象的属性。其中文件上传时,需要将该文件转换中Part对象(filePart),Part对象可以表示一个上传的文件或者表单数据。然后生成一个唯一的ticketId,将附件内容通过IO读取的方式保存在Attachment对象中的二进制数组中,最后将ticketId和Ticket对象组成键值对添加到ticketDatabase中,把页面重定向到浏览单个留言详细内容的页面中

  3.显示:

    jsp页面处理显示操作

部署文件web.xml:

<jsp-config>
        <!-- jsp组属性,不同的jsp组可以设置不同的属性,若不同属性组发送匹配冲突时,遵循匹配精确优先原则 -->
        <jsp-property-group>
            <!-- 该jsp组属性将应用于哪些文件,在这里它将匹配在Web应用程序中所有以jsp和jspf文件结尾的文件-->
            <url-pattern>*.jsp</url-pattern>
            <url-pattern>*.jspf</url-pattern>
            <!-- jsp页面编码,它和page指令中的pagaEncoding特性一致-->
            <page-encoding>UTF-8</page-encoding>
            <!-- 允许使用jsp中的java,若为true,则禁止在jsp中使用java -->
            <scripting-invalid>false</scripting-invalid>
            <!-- 告诉web容器在所有属于该属性组的jsp的头部添加文件/WEB-INF/jsp/base.jspf -->
            <include-prelude>/WEB-INF/jsp/base.jspf</include-prelude>
            <!-- 告诉jsp转换器删除响应输出中的空白,只保留由指令、声明、脚本和其他JSP标签创建的文本,即可以产生干净的代码 -->
            <trim-directive-whitespaces>true</trim-directive-whitespaces>
            <!-- 默认的内容类型是text/html -->
            <default-content-type>text/html</default-content-type>
        </jsp-property-group>
    </jsp-config>

base.jspf:

<%@ page contentType="text/html; charset=utf-8" language="java"%>
<%@ page import="cn.example.Ticket, cn.example.Attachment" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

listTickets.jsp:

<%@ page session="false" import="java.util.Map" %>
<%
    @SuppressWarnings("unchecked")
    Map<Integer,Ticket> ticketDatabase = (Map<Integer, Ticket>)request.getAttribute("ticketDatabase");
%>

<!DOCTYPE html>
<html>
    <head>
        <title>留言板</title>
    </head>
    <body>
        <h2>留言板</h2>
        <a href="
            <c:url value="/tickets">
                <c:param name="action" value="create"/>
            </c:url>
        ">创建留言</a><br/><br/>
        <%
            if(ticketDatabase.size() == 0){
                %><i>留言板中没有留言。</i><%
            }
            else{
                for(int id : ticketDatabase.keySet()){
                    String idString = Integer.toString(id);
                    Ticket ticket = ticketDatabase.get(id);
                    %>留言 #<%= idString %> : <a href="
                        <c:url value="/tickets">
                            <c:param name="action" value="view"/>
                            <c:param name="ticketId" value="<%= idString %>"/>
                        </c:url>
                    "><%=ticket.getSubject() %></a>(用户: <%= ticket.getCustomerName() %>) <br/>
                     <%
                }
            }
        %>
    </body>
</html>

ticketForm.jsp:

package cn.example;
/*
 * 一个简单的POJO,表示着一个附件类
 */
public class Attachment {
    private String name;        // 附件名
    private byte[] contents;    // 附件的内容以字节数组的形式保存

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public byte[] getContents() {
        return contents;
    }
    public void setContents(byte[] contents) {
        this.contents = contents;
    }
}

viewTicket.jsp:

<%
    String ticketId = (String) request.getAttribute("ticketId");
    Ticket ticket = (Ticket) request.getAttribute("ticket");
%>
<!DOCTYPE html>
<html>
    <head>
        <title>留言版</title>
    </head>
    <body>
        <h2>留言 #<%=ticketId %>: <%= ticket.getSubject() %></h2>
        <i>用户 - <%=ticket.getCustomerName() %></i> <br/><br/>
        <i>内容:</i><br/>
        <%= ticket.getBody() %> <br/><br/>
        <%
            if(ticket.getNumberOfAttachments() > 0){
            %>附件:<%
                int i = 0;
                for(Attachment a:ticket.getAttachments()){
                    if(i++ > 0)
                        out.print(", ");
                    %>
                    <a href="
                        <c:url value="/tickets">
                            <c:param name="action" value="download"/>                            <c:param name="ticketId" value="<%= ticketId %>"/>
                            <c:param name="attachment" value="<%=a.getName() %>"/>
                        </c:url>
                        "><%=a.getName() %> </a><%
                }
            }
        %><br/>
        <a href="<c:url value="/tickets"/>">返回留言板主页</a>
    </body>
</html>

Attachment.java

package cn.example;
/*
 * 一个简单的POJO,表示着一个附件类
 */
public class Attachment {
    private String name;        // 附件名
    private byte[] contents;    // 附件的内容以字节数组的形式保存

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public byte[] getContents() {
        return contents;
    }
    public void setContents(byte[] contents) {
        this.contents = contents;
    }
}

Ticket.java:

package cn.example;
/*
 * 一个简单的POJO,表示一个票据类
 */

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

public class Ticket {
    private String customerName; // 用户名
    private String subject; // 评论内容的主题
    private String body;           // 评论内容的主体
    // 使用哈希map表示附件数据库,以附件名为键,以附件为值
    private Map<String, Attachment> attachments = new LinkedHashMap<String, Attachment>();

    public String getCustomerName() {
        return customerName;
    }

    public void setCustomerName(String customerName) {
        this.customerName = customerName;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }

    public Attachment getAttachment(String name){
        return this.attachments.get(name);
    }

    public Collection<Attachment> getAttachments() {
        return this.attachments.values();
    }

    public void addAttachments(Attachment attachment) {
        this.attachments.put(attachment.getName(), attachment);
    }

    public int getNumberOfAttachments(){
        return this.attachments.size();
    }

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

}

TicketServlet.java:

package cn.example;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

import jdk.nashorn.internal.ir.RuntimeNode.Request;
@WebServlet(
        name = "ticketServlet",
        urlPatterns = {"/tickets"},
        loadOnStartup = 1
)
//告诉web容器为该servlet提供文件上传支持
@MultipartConfig(
        // 告诉web容器文件必须达到5MB时才写入临时目录
        fileSizeThreshold = 5_242_800, // 5MB
        // 上传的文件不能超过20MB
        maxFileSize = 20_971_520L, // 20MB
        // 不能接收超过40MB的请求
        maxRequestSize = 41_942_040L // 40MB
)
public class TicketServlet extends HttpServlet{
    private volatile int TICKET_ID_SEQUENCE = 1;
    // 使用哈希map作为票据数据库
    private Map<Integer, Ticket> ticketDatabase = new LinkedHashMap<>();

    /*
     * 在doGet()方法中,根据请求参数的不同,把任务委托给相应的执行器
     * 功能:
     *         显示创建票据页面
     *         查看单个票据内容
     *         下载附件
     *         显示票据列表
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");

        String action = req.getParameter("action");
        // 若不带action请求参数,设置默认值,即默认的行为是显示票据列表
        if(action == null)
            action = "list";
        switch (action) {
            case "create":
                this.showTicketForm(req,resp);
                break;
            case "view":
                this.viewTicket(req, resp);
                break;
            case "download":
                this.downloadAttachment(req, resp);
                break;
            default:
                this.listTickets(req, resp);
                break;
        }
    }

    /*
     * 创建新的票据
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");

        String action = req.getParameter("action");
        if(action == null)
            action = "list";
        switch(action){
            case "create":
                this.createTicket(req, resp);
                break;
            case "list":
            default:
                resp.sendRedirect("tickets");
                break;
        }
    }

    private void createTicket(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException{

        //1.新建一个票据对象
        Ticket ticket = new Ticket();

        //2.以表单数据为源,设置票据相应的成员属性
        ticket.setCustomerName(req.getParameter("customerName"));
        ticket.setSubject(req.getParameter("subject"));
        ticket.setBody(req.getParameter("body"));

        //3.处理文件上传
        Part filePart = req.getPart("file1");
        if(filePart != null && filePart.getSize() > 0){
            Attachment attachment = this.processAttachment(filePart);
            if(attachment != null)
                ticket.addAttachments(attachment);
        }

        int id;
        synchronized (this) {
            id = this.TICKET_ID_SEQUENCE++;
            this.ticketDatabase.put(id, ticket);
        }

        resp.sendRedirect("tickets?action=view&ticketId=" + id);
    }

    private Attachment processAttachment(Part filePart) throws IOException{
        InputStream  inputStream = filePart.getInputStream();
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        int read;
        final byte[] bytes = new byte[1024];

        while((read = inputStream.read(bytes)) != -1){
            outputStream.write(bytes, 0, read);
        }

        Attachment attachment = new Attachment();
        attachment.setName(filePart.getSubmittedFileName());
        attachment.setContents(outputStream.toByteArray());

        return attachment;
    }

    private void listTickets(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
         request.setAttribute("ticketDatabase", this.ticketDatabase);
         request.getRequestDispatcher("/WEB-INF/jsp/view/listTickets.jsp").forward(request, response);
    }

    private void downloadAttachment(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String idString = req.getParameter("ticketId");
        Ticket ticket = this.getTicket(idString, resp);
        if(ticket == null)
            return;

        String name = req.getParameter("attachment");
        if(name == null){
            resp.sendRedirect("tickets?action=view&tickedId=" + idString);
            return;
        }

        Attachment attachment = ticket.getAttachment(name);
        if(attachment == null){
            resp.sendRedirect("tickets?action=view&tickedId=" + idString);
            return;
        }

        // 强制浏览器询问用户是保存还是下载文件,而不是在浏览器打开该文件
        resp.setHeader("Content-Disposition", "attachment; filename = " + attachment.getName());
        // 设置内容类型是通用的、二进制内容类型,这样容器就不会使用字符编码对该数据进行处理
        // 更加准确的应该使用附件的MIME内容类型
        resp.setContentType("application/octet-stream");

        // 使用ServletOutputStream将附件内容输出到响应中
        ServletOutputStream stream = resp.getOutputStream();
        stream.write(attachment.getContents());
    }

    private void viewTicket(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
        String idString = req.getParameter("ticketId");
        Ticket ticket = this.getTicket(idString, resp);
        if(ticket == null)
            return;

        req.setAttribute("ticketId", idString);
        req.setAttribute("ticket", ticket);

        RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/jsp/view/viewTicket.jsp");
        dispatcher.forward(req, resp);
    }

    private Ticket getTicket(String idString, HttpServletResponse resp) throws IOException{
        if(idString == null || idString.length() == 0){
            resp.sendRedirect("tickets");
            return null;
        }
        try{
            Ticket ticket = this.ticketDatabase.get(Integer.parseInt(idString));
            if(ticket == null){
                resp.sendRedirect("tickets");
                return null;
            }
            return ticket;
        }catch(Exception e){
            resp.sendRedirect("tickets");
            return null;
        }
    }

    private void showTicketForm(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
           RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/jsp/view/ticketForm.jsp");
           dispatcher.forward(request, response);
    }
}

运行结果:

空白的留言板:

创建留言:

单个留言的详细信息:

附件下载:

非空白的留言板:

分析:

  1.j在jsp可以结合java和html,方便编写动态页面

   2.在jsp中,可以做几乎所有java类可以完成的事情,这会带来不安全的操作。若jsp开发者不熟悉java,而他们无限制地在jsp中使用java,会带来安全隐患。

   3.表示层(jsp用于开发表示层)需要和业务逻辑层、数据持久层分割。

   4.在jsp中显示动态内容,应该尽量避免使用java代码,可以使用jsp标签库替代java代码

时间: 2024-11-03 21:02:40

Servlet实践--留言板-v1的相关文章

使用JSP、Servlet实现一个简单的留言板

1 留言板的页面 messageBoard.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/htm

Jsp+Servlet+JavaBean学习阶段案例-留言板(MessageBoard)

Jsp+Servlet+JavaBean学习阶段的一个案例总结 名称:留言板(MessageBoard) 开发技术:Jsp+Servlet+JavaBean 数据库:Mysql 开发工具:IntelliJ IDEA 2016.1.3 服务器:Tomcat 8.0.36 功能说明 普通用户:增加删除修改留言信息 管理员:增加删除修改留言信息.管理用户.设置用户发帖留言权限 数据库结构 tb_user表 字段 类型 名称 说明 id int id name nvarchar 姓名 password

JSP+Servlet+JavaBean传统方式实现简易留言板制作(注册、登录、留言)

学JavaEE也有一段时间了,跟着老师和教材做了不少东西,但是一直以来没时间写博客,今天就把以前写的一个简易留言板简单发一下吧. 开发工具 主要用的开发工具为 MyEclipse(2014.2016均可).Tomcat 7.0.SQL Server 2016.SSMS数据库管理工具.浏览器等. 开发环境 开发环境为windows系统,已安装配置Java最新版开发环境. 主要功能与语言 登录.注册.并可以在留言板留言,所有留言内容均可见. 所采用JSP+Servlet+JavaBean传统方式,仅

欣欣的留言板项目====超级触动的dbUtil实现留言板

留言板管理系统 我的完成效果图: 提交后: 我的留言板基本架构如图: 创建留言板数据库: 刚开始我的前台主页中写留言信息表单: <body> <h1>留言板</h1> <form action="提交后的页面地址" method="post" > 留言者:<input type="text" name="author" /> 留言的内容:<input type=

JSP实现 留言板 分页显示,新留言显示在第一个~

页面效果图: 留言Servlet-----MessageServlet package cn.MuJH.newsManager.servlet; import java.io.IOException;import java.text.SimpleDateFormat;import java.util.Date;import java.util.List; import javax.servlet.ServletException;import javax.servlet.annotation.W

ABP vNext模块系统初试 - 创建留言板模块

上次翻译的ABP vNext介绍发布后,引起了很多ABP好爱者的关注. 那么就趁热打铁,体验一下新ABP. 新的ABP中我最感兴趣的是它的模板系统,所以这次就利用模块系统做了留言板的例子,分享给大家. 留言板模块 我们的留言板模块功能很简单,就是提供用户留言的功能(废话),为了简单起见,功能都非常简陋: 通过菜单进入留言板 显示留言一览,显示的项目有:标题,作者和时间 任意用户可创建留言,修改或删除自己的留言 管理员可修改或删除任何留言 留言只有标题和正文,均为纯文本(不支持富文本). 使用AB

php实现简易留言板效果

首先是Index页面效果图 index.php <?php header('content-type:text/html;charset=utf-8'); date_default_timezone_set('PRC'); $filename="msg.txt"; $msgs=[]; //检测文件是否存在 if(file_exists($filename)){ //读取文件中的内容 $string=file_get_contents($filename); if(strlen($

k8s官方案例练习-使用 Redis 部署 PHP 留言板应用程序

[[email protected] guestbook]# cat redis-master-deployment.yaml apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2 kind: Deployment metadata: name: redis-master labels: app: redis spec: selector: matchLabels: app: redis role: master tie

会话控制之留言板

//要用session做一个留言板 //首先先做一个loginn.php文件  做出个用户可以输入的表单 //将这个表单提交给另一个页面dlchuli.php对用户输入的用户名和密码进行处理 //在这个dlchuli.php页面中先开启session //运用post方法来获取到用户输入的用户名和密码 //再通过用户输入的用户名和密码从数据库里找需要匹配的数据信息 //这个页面就是来确认下用户输入的信息是否正确,若正确就跳转到主页面.mainn.php //在建一个主页面来显示留言和发布留言消息