功能介绍:

  由三个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代码

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

  1. JSP简易留言板

    写在前面 在上篇博文JSP内置对象中介绍JSP的9个内置对象的含义和常用方法,但都是比较理论的知识.今天为大家带来一个小应用,用application制作的简易留言板. 包括三个功能模块:留言提交.留 ...

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

    留言板管理系统 我的完成效果图: 提交后: 我的留言板基本架构如图: 创建留言板数据库: 刚开始我的前台主页中写留言信息表单: <body> <h1>留言板</h1> ...

  3. php实现简易留言板效果

    首先是Index页面效果图 index.php <?php header('content-type:text/html;charset=utf-8'); date_default_timezo ...

  4. Operator 示例:使用 Redis 部署 PHP 留言板应用程序

    「Kubernetes 官方示例:使用 Redis 部署 PHP 留言板应用程序」Operator 化. 源码仓库:https://github.com/jxlwqq/guestbook-operat ...

  5. AngularJs学习笔记(制作留言板)

    原文地址:http://www.jmingzi.cn/?post=13 初学Anjularjs两天了,一边学一边写的留言板,只有一级回复嵌套.演示地址 这里总结一下学习的过程和笔记.另外,看看这篇文章 ...

  6. dd——留言板再加验证码功能

    1.找到后台-核心-频道模型-自定义表单 2.然后点击增加新的自定义表单 diyid 这个,不管他,默认就好 自定义表单名称 这个的话,比如你要加个留言板还是投诉建议?写上去呗 数据表  这个不要碰, ...

  7. asp.net留言板项目源代码下载

    HoverTree是一个asp.net开源项目,实现了留言板功能. 前台体验网址:http://hovertree.com/guestbook/ 后台请下载源代码安装. 默认用户名:keleyi 默认 ...

  8. html的留言板制作(js)

    这次留言板运用到了最基础的localstorage的本地存储,展现的效果主要有: 1.编写留言2.留言前可以编辑自己的留言昵称.不足之处: 1.未能做出我喜欢的类似于网易的叠楼功能. 2.未能显示评论 ...

  9. 11月8日PHP练习《留言板》

    一.要求 二.示例页面 三.网页代码及网页显示 1.denglu.php  登录页面 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Tran ...

随机推荐

  1. ubuntu-17.10 安装 FANN

      因为想用C语言写神经网络,不用已有的库的话,又太难了,所以准备安装一个夸平台的FANN库, 源文件下载地址http://leenissen.dk/fann/wp/download/,我下载的是最新 ...

  2. Leetcode题解(26)

    80. Remove Duplicates from Sorted Array II 题目 分析:简单的操作,代码如下: class Solution { public: int removeDupl ...

  3. Android 开发笔记___Activity的生命周期

    一个activity就是一个页面,入口函数是oncreate(). onCreate:创建页面,把页面上各个元素加载到内存 onStart:开始页面,把页面显示在屏幕 onResume:恢复页面,让页 ...

  4. php header设置页面编码

    php header设置页面编码 未分类 PHP 页面编码声明方法详解(header或meta) http://www.jb51.net/article/22501.htm PHP 页面编码声明与用h ...

  5. Js的闭包,这篇写的是比较清晰明了的

    一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量 ...

  6. WIN7+wampserver2.4+zend stadio10.6.1配置Xdebug

    一.前言 zend stadio调试很不方便,php5.3版本之前可使用zend debuger调试,php5.3以后就需要使用XDebug调试了.下面介绍我配置的经验,希望帮助更多的人 二.配置步骤 ...

  7. 手工搭建基于ABP的框架(2) - 访问数据库

    为了防止不提供原网址的转载,特在这里加上原文链接: http://www.cnblogs.com/skabyy/p/7517397.html 本篇我们实现数据库的访问.我们将实现两种数据库访问方法来访 ...

  8. WebSocket小插件

    一.WebSocket小介绍 随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通信 ...

  9. [转载] Hive结构

    转载自http://www.csdn.net/article/2010-11-28/282616 Hive 体系结构 Hive 的结构如图所示 主要分为以下几个部分: 用户接口,包括 CLI,Clie ...

  10. [转载] 运维角度浅谈:MySQL数据库优化

    一个成熟的数据库架构并不是一开始设计就具备高可用.高伸缩等特性的,它是随着用户量的增加,基础架构才逐渐完善. 作者:zhenliang8,本文转自51CTO博客,http://lizhenliang. ...