JSP实用教程之简易页面编辑器的实现方法(附源码)

前言

实现一个简易的页面编辑器是大家在学习jsp的时候经常会遇到的一个需求,发现网上这方便的资料不多,所以想着自己总结下,本文详细介绍了JSP简易页面编辑器的实现方法,下面话不多说,来一起看看详细的介绍:

需求

提供一页面,放置“帮助”、“版权”文字内容,特点:静态页面,无须读数据库,只是应付字眼上频繁的修改;没有复杂的交互,无须 JavaScript;没有图片,不需要文件上传。

给出的方案:提供一页面和简易的后台管理,功能单一,只是编辑页面(只是修改字体、大小、粗体、斜体等的功能)。
实现思路:纯 JSP 展示,管理界面用 HTTP Basic 登入,通过一个 js 写成 HTML 编辑器修改页面内容。直接修改服务器磁盘文件。

界面如下,右图是后台编辑。

值得一提的是,Tomcat 7 下 JSP 默认的 Java 语法仍旧是 1.6 的。在 JSP 里面嵌入 Java 1.7 特性的代码会抛出“Resource specification not allowed here for source level below 1.7”的异常。于是需要修改 Tomcat/conf/web.xml 里面的配置文件,找到 <servlet> 节点( <servlet-name>jsp</servlet-name>  的才是),加入下面最后两个 init-param 节点部分。注意是 <servlet-name>jsp</servlet-name> 节点才可以,不是 default 节点(很相似)。

<servlet>
  <servlet-name>jsp</servlet-name>
  <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
  <init-param>
   <param-name>fork</param-name>
   <param-value>false</param-value>
  </init-param>
  <init-param>
   <param-name>xpoweredBy</param-name>
   <param-value>false</param-value>
  </init-param> 

  <init-param>
   <param-name>compilerSourceVM</param-name>
   <param-value>1.7</param-value>
  </init-param>
  <init-param>
   <param-name>compilerTargetVM</param-name>
   <param-value>1.7</param-value>
  </init-param> 

  <load-on-startup>3</load-on-startup>
 </servlet> 

访问的 jsp 其实只有两个 /index.jsp 和 /admin/index.jsp,分别是静态页面和后台编辑页面。/admin/action.jsp 用于接收保存的 action,数据由表单 POST 过来。functions.jsp 就是全部的业务逻辑代码,通过 %@include file="functions.jsp"% ,它不能单独给外界 url 访问。

我们先看看 /index.jsp。

<%@page pageEncoding="UTF-8"%>
<html>
 <head>
  <title>帮助</title>
  <meta charset="utf-8" />
   <!--宽度 320px -->
  <meta name="viewport" content="width=320,user-scalable=0,initial-scale=1.0,maximum-scale=1.0, minimum-scale=1.0" />
  <style>
html {
 font-size: 15px;
} 

body {
 padding: 0;
 margin: 0 auto;
 max-width: 600px;
 -webkit-font-smoothing: antialiased;
 font-family: "Microsoft YaHei", "ff-tisa-web-pro-1", "ff-tisa-web-pro-2",
  "Lucida Grande", "Hiragino Sans GB", "Hiragino Sans GB W3", Arial;
 background-color: #ebebeb;
} 

h1 {
 text-align: center;
 font-size: 1.5rem;
 letter-spacing: 2px;
 color: #864c24;
 border-bottom: #e0c494 solid 1px;
 padding: 2% 0;
} 

h2 {
 font-size: 1rem;
 letter-spacing: 1px;
 color: #4c4c4c;
 padding-bottom:0;
 margin: 0;
} 

p {
 text-align: justify;
 font-size: 1rem;
 color: #818181;
 margin: 1% 0;
 margin-top:0;
} 

ol {
 padding: 0;
 margin: 0;
} 

ol { 

} 

ol>li>:first-child {
 /* Make Firefox put the list marker inside */
 /* https://bugzilla.mozilla.org/show_bug.cgi?id=36854 "if list-style-position is inside, bullet takes own line" */
 display: inline;
} 

ol>li>:first-child:after {
 /* Add the margin that was lost w/ display: inline */
 /* Firefox 10 displays this as block */
 /* Safari 5.1.2 and Chrome 17.0.963.56 don't */
 content: "";
 display: block;
} 

li {
 padding: 5% 2%;
 list-style-position: inside;
 border-bottom: 1px solid #dddddb;
} 

.text {
 color: #a8a8a8;
 font-size: 1rem;
 font-weight: bold;
 padding: 2%;
}
</style>
 </head>
 <body>
  <!-- Editable AREA|START -->  <h1>帮助</h1>
  <div class="text">常见问题</div>
  <ol>
   <li>
    <h2>Power TV的资费是怎样收取的?</h2>
    <p>12元Power TV手机电视包月,产品代码88888888,12元/月;省内用户省内使用配送3G/月定向流量,流量仅用于使用Power TV,超过定向流量部分按标准资费收取; </p>
   </li>
   <li>
    <h2>Power TV的资费是怎样收取的?</h2>
    <p>12元Power TV手机电视包月,产品代码88888888,12元/月;省内用户省内使用配送3G/月定向流量,流量仅用于使用Power TV,超过定向流量部分按标准资费收取; </p>
   </li>
   <li>
    <h2>Power TV的资费是怎样收取的?</h2>
    <p>12元Power TV手机电视包月,产品代码88888888,12元/月;省内用户省内使用配送3G/月定向流量,流量仅用于使用Power TV,超过定向流量部分按标准资费收取; </p>
   </li>
   <li>
    <h2>Power TV的资费是怎样收取的?</h2>
    <p>12元Power TV手机电视包月,产品代码88888888,12元/月;省内用户省内使用配送3G/月定向流量,流量仅用于使用Power TV,超过定向流量部分按标准资费收取; </p>
   </li>
   <li>
    <h2>Power TV的资费是怎样收取的?</h2>
    <p>12元Power TV手机电视包月,产品代码88888888,12元/月;省内用户省内使用配送3G/月定向流量,流量仅用于使用Power TV,超过定向流量部分按标准资费收取; </p>
   </li>
   <li>
    <h2>Power TV的资费是怎样收取的?</h2>
    <p>12元Power TV手机电视包月,产品代码88888888,12元/月;省内用户省内使用配送3G/月定向流量,流量仅用于使用Power TV,超过定向流量部分按标准资费收取; </p>
   </li>
   <li>
    <h2>Power TV的资费是怎样收取的?</h2>
    <p>12元Power TV手机电视包月,产品代码88888888,12元/月;省内用户省内使用配送3G/月定向流量,流量仅用于使用Power TV,超过定向流量部分按标准资费收取; </p>
   </li>
  </ol>
  <!-- Editable AREA|END -->
 </body>
</html> 

这份 JSP 与一般 JSP 并无特异,只不过大家有没有留意到两段注释: <!-- Editable AREA|START --> <!-- Editable AREA|END --> ——这就是我们约定的“可编辑”范围。当然,使用自定义的 HTML Tag 也可以,只要定义了一个范围即可。一份网页,无非是 HTML。对于其中欲编辑的东西,我们定义一个范围指明哪些地方需要编辑,就可以了。至于为什么不让全部的页面可以编辑?是因为我们不想用户对页面其它部分进行编辑,万一修改了的关键地方造成了错误,那可不好。

好了,怎么让这个 /index.jsp 编辑呢?就是利用 Java 读取磁盘的方法来做的。在这个之前,得先登录到 /admin/index.jsp。这里我们通过 HTTP Basic Authorization 来做用户认证,无须数据库。如果需要修改 账号密码,打开 admin/functions.jsp,编辑头部分即可:

<%!
 public static final String userid = "admin", pwd = "123123";
 ....
%>

不过笔者调试 HTTP Basic Authorization 遇到了个小问题,就是浏览器弹出的对话框,不知怎么修改其中的提示文字,试过几种方法,要么不显示,要么乱码。如果知道的童鞋还请告知一二!

action.jsp 也要作认证的限制,不然等于是个漏洞可以让别人 POST 任何数据到页面。

<%@page pageEncoding="UTF-8"%>
<%@include file="functions.jsp"%>
<% 

if (checkAuth(request.getHeader("Authorization"), userid, pwd)) {
 request.setCharacterEncoding("utf-8");
 if (request.getMethod().equalsIgnoreCase("POST")) {
  String contentBody = request.getParameter("contentBody"), path = Mappath(getEditJSP(request));
  System.out.println("path:::" + path);
  save_jsp_fileContent(path, contentBody);
  out.println("<script>alert('修改成功!');window.location = document.referrer;</script>");
 } else {
  out.println("method error");
 }
} else {
 %>
 <html>
 <body>
  非法登录!
 </body>
 </html>
 <%
}
%> 

修改下页面,点击保存就可以修改页面了。

至于 HTML 如何编辑?这个答案想必大家都清楚,使用 HTML 可视化编辑器即可,在线的哦,而不是什么 Dreamweaver、FrontPage、VS Web 之类啦。老人们用过的就是有 FCKEditror 呀、TinyMCE Editor,近几年好像喜欢用国产了,我就不知道了。现在这个用的是我自己写,功能比较单一的。

核心逻辑是通过下面的代码搞定的。

<%@page pageEncoding="UTF-8" import="sun.misc.BASE64Decoder, java.io.*"%>
<%!
public static final String userid = "admin", pwd = "86006966";
// 检查 HTTP Basic 认证 

 /**
  * 是否空字符串
  *
  * @param str
  * @return
  */
 public static boolean isEmptyString(String str) {
  return str == null || str.trim().isEmpty();
 } 

 /**
  * 是否不合法的数组
  *
  * @param arr
  * @return
  */
 public static boolean isBadArray(String[] arr) {
  return arr == null || arr.length != 2;
 } 

 /**
  *
  * @param authorization
  *   认证后每次HTTP请求都会附带上 Authorization 头信息
  * @param username
  *   用户名
  * @param password
  *   密码
  * @return true = 认证成功/ false = 需要认证
  */
 public static boolean checkAuth(String authorization, String username, String password) {
  if (isEmptyString(authorization))
   return false; 

  String[] basicArray = authorization.split("\\s+");
  if (isBadArray(basicArray))
   return false; 

  String idpass = null;
  try {
   byte[] buf = new BASE64Decoder().decodeBuffer(basicArray[1]);
   idpass = new String(buf, "UTF-8");
  } catch (IOException e) {
   e.printStackTrace();
   return false;
  } 

  if (isEmptyString(idpass))
   return false; 

  String[] idpassArray = idpass.split(":");
  if (isBadArray(idpassArray))
   return false; 

  return username.equalsIgnoreCase(idpassArray[0]) && password.equalsIgnoreCase(idpassArray[1]);
 } 

 /**
  * 可编辑标识开始
  */
 private final static String startToken = "<!-- Editable AREA|START -->"; 

 /**
  * 可编辑标识结束
  */
 private final static String endToken = "<!-- Editable AREA|END -->"; 

 /**
  * 根据 页面中可编辑区域之标识,取出来。
  *
  * @param fullFilePath
  *   完整的 jsp 文件路径
  * @return 可编辑内容
  * @throws IOException
  */
 public static String read_jsp_fileContent(String fullFilePath) throws IOException {
  String jsp_fileContent = readFile(fullFilePath); 

  int start = jsp_fileContent.indexOf(startToken), end = jsp_fileContent.indexOf(endToken); 

  try {
   jsp_fileContent = jsp_fileContent.substring(start + startToken.length(), end);
  } catch (StringIndexOutOfBoundsException e) {
   jsp_fileContent = null; 

   String msg = "页面文件" + fullFilePath + "中没有标记可编辑区域之标识。请参考:" + startToken + "/" + endToken;
   throw new IOException(msg);
  } 

  return jsp_fileContent;
 } 

 /**
  * 请求附带文件参数,将其转换真实的磁盘文件路径
  *
  * @param rawFullFilePath
  *   URL 提交过来的磁盘文件路径,可能未包含文件名或加了很多 url 参数
  * @return 完整的磁盘文件路径
  */
 static String getFullPathByRequestUrl(String rawFullFilePath) {
  if (rawFullFilePath.indexOf(".jsp") == -1)
   rawFullFilePath += "/index.jsp"; // 加上 扩展名 

  if (rawFullFilePath.indexOf("?") != -1) // 去掉 url 参数
   rawFullFilePath = rawFullFilePath.replaceAll("\\?.*$", ""); 

  return rawFullFilePath;
 } 

 /**
  * 保存要修改的页面
  *
  * @param rawFullFilePath
  *   真实的磁盘文件路径
  * @param newContent
  *   新提交的内容
  * @throws IOException
  */
 public static void save_jsp_fileContent(String rawFullFilePath, String newContent) throws IOException {
  String fullFilePath = getFullPathByRequestUrl(rawFullFilePath); // 真实的磁盘文件路径
  String jsp_fileContent = readFile(fullFilePath), toDel_fileContent = read_jsp_fileContent(fullFilePath);// 读取旧内容
//System.out.println(jsp_fileContent);
//System.out.println(toDel_fileContent);
  if (toDel_fileContent != null) {
   jsp_fileContent = jsp_fileContent.replace(toDel_fileContent, newContent);
   save2file(fullFilePath, jsp_fileContent); // 保存新内容
  } else {
   throw new IOException("页面文件中没有标记可编辑区域之标识。请参考: startToken/endTpoken");
  }
 } 

 /**
  * 读取文件
  *
  * @param filename
  * @return
  * @throws IOException
  */
 public static String readFile(String filename) throws IOException {
  File file = new File(filename);
  if (!file.exists())
   throw new FileNotFoundException(filename + " 不存在!"); 

  try (FileInputStream is = new FileInputStream(file);) {
   String line = null;
   StringBuilder result = new StringBuilder(); 

   try (InputStreamReader isReader = new InputStreamReader(is, "UTF-8");
     BufferedReader reader = new BufferedReader(isReader);) {
    while ((line = reader.readLine()) != null) {
     result.append(line);
     result.append('\n');
    }
   } catch (IOException e) {
    System.err.println(e);
   } 

   return result.toString();
  } catch (IOException e) {
   System.err.println("讀取文件流出錯!" + filename);
   throw e;
  }
 } 

 /**
  * 写文件不能用 FileWriter,原因是会中文乱码
  *
  * @param filename
  * @param content
  * @throws IOException
  */
 public static void save2file(String filename, String content) throws IOException {
  try (FileOutputStream out = new FileOutputStream(filename);
    // OutputStreramWriter将输出的字符流转化为字节流输出(字符流已带缓冲)
    OutputStreamWriter writer = new OutputStreamWriter(out, "UTF8");) {
   writer.write(content);
  } catch (IOException e) {
   System.err.println("写入文件" + filename + "失败");
   throw e;
  }
 } 

 /**
  * 输入一个相对地址,补充成为绝对地址 相对地址转换为绝对地址,并转换斜杠
  *
  * @param relativePath
  *   相对地址
  * @return 绝对地址
  */
 public String Mappath(String relativePath) {
  String absoluteAddress = getServletContext().getRealPath(relativePath); // 绝对地址 

  if (absoluteAddress != null)
   absoluteAddress = absoluteAddress.replace('\\', '/');
  return absoluteAddress;
 } 

 public String getEditJSP(HttpServletRequest request) {
  String uri = request.getRequestURI().replaceAll("admin/\\w+", "index");
  uri = uri.replace(request.getContextPath(), "");
  return uri;
 }
%> 

用户凭账号密码登入简易的后台,通过可视化编辑器即可修改页面内容,立刻修改,立刻产生效果,简单快捷——把页面开放出来允许自主编辑这样会提高效率——减少来回修改的次数。

完整源码下载:http://xiazai.jb51.net/201707/yuanma/jsp-page(jb51.net).rar

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • JSP实用教程之简易页面编辑器的实现方法(附源码)

    前言 实现一个简易的页面编辑器是大家在学习jsp的时候经常会遇到的一个需求,发现网上这方便的资料不多,所以想着自己总结下,本文详细介绍了JSP简易页面编辑器的实现方法,下面话不多说,来一起看看详细的介绍: 需求 提供一页面,放置"帮助"."版权"文字内容,特点:静态页面,无须读数据库,只是应付字眼上频繁的修改:没有复杂的交互,无须 JavaScript:没有图片,不需要文件上传. 给出的方案:提供一页面和简易的后台管理,功能单一,只是编辑页面(只是修改字体.大小.粗

  • jQuery+jsp下拉框联动获取本地数据的方法(附源码)

    本文实例讲述了jQuery+jsp下拉框联动获取本地数据的方法.分享给大家供大家参考,具体如下: JQuery下拉框联动很好的体现了Ajax的按需取数据的要求,减小数据的交互量.(点击此处下载源代码) 下面的实例使用Json将服务器端的类或者对象转换为JSON格式,主要运用了6个jar包 commons-beanutils-1.7.0.jar commons-collections-3.2.jar commons-lang-2.3.jar commons-logging-1.0.4.jar ez

  • JSP实用教程之简易图片验证码的实现方法(附源码)

    前言 很多新手对图片验证码不是很了解,所以本文尝试通过一个简单的 JSP 小程序来实现验证码功能.文中给出了详细的示例代码,文末给出了完整实例代码的下载地址,下面话不多说了,来一起看看详细的介绍吧. 效果图 示例代码 前台代码如下: <form action="action.jsp" method="POST"> <label> 用户名: <input type="text" name="name"

  • JSP实用教程之简易文件上传组件的实现方法(附源码)

    前言 本文主要给大家介绍的是关于JSP简易文件上传组件的实现方法,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍吧. 文件上传,包括但不限于图片上传,是 Web 开发中司空见惯的场景,相信各位或多或少都曾写过这方面相关的代码.Java 界若说文件上传,则言必称 Apache Commons FileUpload,论必及  SmartUpload.更甚者,Servlet 3.0 将文件上传列入 JSR 标准,使得通过几个注解就可以在 Servlet 中配置上传,无须依赖任何组件.使用第

  • 微信小程序实现点击文字页面跳转功能【附源码下载】

    本文实例讲述了微信小程序实现点击文字页面跳转功能.分享给大家供大家参考,具体如下: 1.效果展示 2.关键代码 index.js文件 Page({ data:{ // text:"这是一个页面" }, onLoad:function(options){ // 页面初始化 options为页面跳转所带来的参数 }, onReady:function(){ // 页面渲染完成 }, onShow:function(){ // 页面显示 }, onHide:function(){ // 页面

  • 详解SpringBoot集成jsp(附源码)+遇到的坑

    本文介绍了SpringBoot集成jsp(附源码)+遇到的坑 ,分享给大家 1.大体步骤 (1)创建Maven web project: (2)在pom.xml文件添加依赖: (3)配置application.properties支持jsp (4)编写测试Controller (5)编写JSP页面 (6)编写启动类App.java 2.新建SpringInitialzr 3.pom文件 <dependencies> <dependency> <groupId>org.s

  • 基于jquery实现可定制的web在线富文本编辑器附源码下载

    今天给大家介绍一款非常棒的WEB在线富文本编辑器--UMeditor,它是由百度web前端研发部开发所见即所得富文本web编辑器UEditor演变的迷你版编辑器,具有轻量,可定制,注重用户体验等特点,允许自由使用和修改代码,适用于前台快速简单回复框或后台内容编辑器. 在线预览     源码下载 如何使用? 建立一个demo.html文件,首先在需要添加编辑器的地方加入以下代码,使用style可以设置编辑器的宽度和高度. <script type="text/plain" id=&

  • 超详细的php用户注册页面填写信息完整实例(附源码)

    注册页面是大多数网站必备的页面,所以很有必要对自己的注册页面做些精心的设计.下面三张图,第一张是注册的展示页面,第二张思维导图就一个简单的逻辑,第三张是通过firebug查看调用的JS文件. 一.给每个输入框写下说明 在用户看到这个输入框的时候,就能非常清晰的明白这个输入框是做啥用的,最大限度的降低他们产生疑惑的可能性.我们需要假设用户毫不了解注册需要输入的内容,随后给他们足够的信息以便帮助他们理解.  二.小图标icon Icon是增强内容的工具,而且能给访客一个很好的暗示.以前使用小图标都是

  • C# Winform调用百度接口实现人脸识别教程(附源码)

    百度是个好东西,这篇调用了百度的接口(当然大牛也可以自己写),人脸检测技术,所以使用的前提是有网的情况下.当然大家也可以去参考百度的文档. 话不多说,我们开始: 第一步,在百度创建你的人脸识别应用 打开百度AI开放平台链接: 点击跳转百度人脸检测链接,创建新应用 创建成功成功之后.进行第二步 第二步,使用API Key和Secret Key,获取 AssetToken 平台会分配给你相关凭证,拿到API Key和Secret Key,获取 AssetToken 接下来我们创建一个AccessTo

  • java实现简易超市管理系统 附源码下载

    java超市管理系统 1.0(含源文件,后续会继续优化~) 前言 一个月零零散散的时间学习了java,通过这次"超市管理系统"的练习,希望可以给一同开始学习java的朋友一些参考,更希望大佬们多多指点和批评~ 一.确定需求 程序概述: 小型超市商品销售管理系统选择小型超市的四类商品进行管理. 这四类商品是:食品.化妆品.生活用品和饮料(四个类). 每类商品都包含有商品名和商品利润 (其中包括商品的售价.进价.库存量).(五个属性) 每类不同的商品还有区别于其他商品的特殊信息(子类特有属

随机推荐