JSP 实用程序之简易页面编辑器

完整源码下载:http://pan.baidu.com/s/1bpkHNnp(百度云提供)

需求:提供一页面,放置“帮助”、“版权”文字内容,特点:静态页面,无须读数据库,只是应付字眼上频繁的修改;没有复杂的交互,无须 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> 节点,加入下面粗体部分才可以。注意是 jsp 节点,不是 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>
<strong>        <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></strong>
        <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;
	}
%>

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

不足之处,还请大家指出。

时间: 2024-10-09 06:17:11

JSP 实用程序之简易页面编辑器的相关文章

JSP 实用程序之简易文件上传组件

源码下载:http://pan.baidu.com/s/1dFK58Tr (百度云提供) 文件上传,包括但不限于图片上传,是 Web 开发中司空见惯的场景,相信各位或多或少都曾写过这方面相关的代码.Java 界若说文件上传,则言必称 Apache Commons FileUpload,论必及  SmartUpload.更甚者,Servlet 3.0 将文件上传列为 JSR 标准,使得通过几个注解就可以在 Servlet 中配置上传,无须依赖任何组件.使用第三方组件或 Servlet 自带组件固然

JSP添加打印部分页面

打印a.jsp的部分页面 将影响要打印的内容放到<div id="printcontent">中 为想要打印的内容创建样式pirnt.css 在a.jsp中添加以下内容: function printpage() { OpenWindow=window.open("", "_blank"); //重写网页 OpenWindow.document.write("<head>"); OpenWindow.d

过滤器为JSP文件生成静态页面

用过滤器为JSP文件生成静态页面,这只是一个简单的实例,客户端浏览器第一次请求JSP页面时,服务器将生成对应的HTML文件,以后访问同一JSP文件,将转为访问生成的HTML文件.一.过滤器 package com.kenfor.lyb.toHtmlfilter; import java.io.*; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import javax.s

简易文本编辑器(Delphi源码)

计划一个简略单纯的文本编纂器,具有建立.编纂.生存平凡文本文件的功能. unit U nit1; interface uses  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,  Dialogs, StdCtrls, ExtCtrls; type  TForm1 = class(TForm)    Panel1: TPanel;    Memo1: TMemo;    OpenDialog1:

zencart简易页面ezpage后台编辑位置

zencart简易页面ezpage后台编辑位置: 后台-Tools(工具)-EZ-Pages(简易页面管理) 若编辑之后无法保存,说明includes\languages\语言包\html_includes目录及子目录下php文件不可写. 解决办法: 登录FTP,将html_includes目录及子目录下的php文件属性设为777即可. 为了安全,编辑完成之后,要将php文件属性设为755.

jsp同一应用中页面间传值方式

JSP页面间传递参数是经常需要使用到的功能,有时还需要多个JSP页面间传递参数.下面介绍一下实现的方法. (1)直接在URL请求后添加 如:< a href="thexuan.jsp?action=transparams&detail=directe">直接传递参数< /a> 特别的在使用response.sendRedirect做页面转向的时候,也可以用如下代码: response.sendRedirect("thexuan.jsp?acti

Jsp与servlet之间页面跳转及参数传递实例(转)

原网址:http://blog.csdn.net/ssy_shandong/article/details/9328985 11. jsp与servlet之间页面跳转及参数传递实例 分类: Java Web2013-07-17 16:24 16461人阅读 评论(12) 收藏 举报 jsp servlet传递参数 目录(?)[-] 1从一个jsp页面跳转到另一个jsp页面时的参数传递 2jsp页面传递参数给servlet 3从servlet到jsp页面中传递参数 4从一个servlet到另一个s

JSP隐式对象是JSP容器为每个页面提供的Java对象

JSP 隐式对象 JSP隐式对象是JSP容器为每个页面提供的Java对象,开发者可以直接使用它们而不用显式声明.JSP隐式对象也被称为预定义变量. JSP所支持的九大隐式对象: 对象 描述 request HttpServletRequest类的实例 response HttpServletResponse类的实例 out PrintWriter类的实例,用于把结果输出至网页上 session HttpSession类的实例 application ServletContext类的实例,与应用上

C++ mfc 简易文本编辑器 遇到的一些问题

[题目40]简易文本编辑器. 设计一个简易的文本编辑器. 设计要求: (1) 具有图形菜单界面: (2) 查找,替换(等长,不等长),插入(插串,文本块的插入).文本块移动(行块,列块移动),删除; (3) 可正确存盘.取盘: (4) 正确显示总行数. 这是题目.................. 使用基于对话框的MFC   我想到的感觉有问题的点! 查找替换 算法不了解  最烦人的时文本块移动 百度好多业找不到!!呵呵难受的是在吾爱发个两个帖子求一下思路 被狗骂了那个人是真的神精符附上链接ht