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

来源:互联网 发布:服装商品数据分析 编辑:程序博客网 时间:2024/05/29 07:35

完整源码下载: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> 节点(<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;}%>

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

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

1 0