这里算是总结一下,这两天的工作,也是自己动手尝试并实现了自己的想法一个案例。
情况大致是这样的:
新上线的webgame需要做一个官网,做好了并上线了(切割、程序、后台,后台使用是java版本的jeecms),但仅仅是自己家的官网做好了,现在上面的想法是需要把这个官网也整理成一个本地能运行的网站给其它游戏的联合运营商以便减少他们工作量(需要全部静态化,所有绝对地址必须改成相对的,图片也必须请求本地指定目录)。最初只是说把当时切割好的静态页面给他们,后来觉得不是很好的一种方案,希望一些不会变的页面,如:游戏的攻略、玩家、常用活动、排行、热门道具等,将这些页面也弄成一个静态的,这样联合运营商就不再需要进行编辑。当准备开始做的时候面临的问题:
1、静态页里面没有数据,即内容模板页content.html中的内容为空;
2、如果便于对整个静态网站的控制,例如:头部、左侧、底部、导航只需要改一处其它所有页面都可以应用并生效;
3、很多文章中的图片是来自主站的,或是自己在后台上传的,如何抓取这些图片并在相应的内容页进行替换;
应对方案:
1、写个爬虫程序,抓取线上所有的静态页面;
2、将共用的部分使用nginx的ssi include语法包含进去;
3、先抓取所有图片,保留其后缀,然后用editplus打开所有文件,用一个正则进行全局替换;
但在第一点与第二点上有一个矛盾点,什么矛盾点?既然是静态页面,后台生成的时候静态页的时候已经没有include,看到的全部是字符串了,因为nginx已经处理过了。
这样只能绕开请求静态页,让爬虫抓取动态页面(需要修改nginx的nginx.conf配置,在请求后台接口时关闭ssi---ssi off;),在模板中使用ssi include语法嵌入共用页。例如文章编辑完成后,后台有一个预览地址:abc.jsp对应前台访问地址页为abc.html,抓取abc.jsp里面的内容,这个时候就能得到如下的内容:
<html>
…
<!—#include virtual=”….html”—>
..
</html>
然后根据请求的地址,保存为相应的目录层次关系,文件名保留,后缀名为.html
到这差不多算是可以将所有静态页请求下来了,但又面临新的问题,抓取并生成的静态页面中所有的地址都是公网上的死链接,需要将这些链接替换掉。例如公网为http://aa.123.com,我先将所有http://aa.123.com的链接src/href替换为”/”---直接指向根目录,然后在后面发现如果写死根目录,提供出去的就有有问题,如果有这些一家运营商,它的官网地址将是http://a.abc.com/game/gamename/,它的服务器配置可能是直接将game指向一个目录,如果用/则直接指向到了http://a.abc.com/ 这显示是有问题的。那就再根据层次关系加../了,如果是一级目录则使用“./”如果是二级目录则使用“../”依次类推…
到这里,似乎应该能正常工作了,但还没有完,来了新问题。前面使用ssi include共用页,例如nav.html,这个页面可能有引用一些css、js,它的链接地址写的也是相对的,这样会导致请求这些文件时路径不对,出现404,而这个还不是重点,最要命的是nginx里不支持<!—#include virtual=”../nav.html”-->这样的写法,会在nginx的logs目录下的error.log文件中输出unsafe uir的信息(相对目录”./”它是支持的),如下图所示:
这下可真没得玩了,我只再写程序将所有include的地方写成“/”(根目录)了,如果有联合运营商要修改,我大不了修改一个正则,然后重新生成一个打包文件。因为include使用提“/”所以里面请求css、js的路径可以使用相对的也没问题。
到此为止,“全静态”的游戏官网能正常跑起来了。(关于图片的处理方案,上面已经写过了)
这个时候把这zip包如果发给其它联合运营商,它们在本地是无法跑起来了,因为里面有include,我的想法是希望有个小应用程度,接收的人双击一下点击后就能直接浏览游戏官网了。这是我的想法,到这里才回到这篇文章的标题上来。
用.net写一个windows应用程序,还是用hta处理呢,想了一下还是使用hta写了,虽然之前用.net写过类似的windows应用程序(选择指定目录,查找这个文件夹下所有execl中每一个sheet中每一个单元格是否包含需要查找的字符串并记录下,运行完后打开一个html页,html内容上需要显示匹配到的行、列和excel文件链接等信息)。
用.hta有个问题就是在装有360的机器上会不停的访问是否要运行此应用程序,比较讨厌~(反正我机器啥“杀毒”的软件也没装)
初步有想法:
1、读取用户的hosts文件信息,查找文件中是否配置过指定的规则,例如127.0.0.1 www.test.com,没添加过则追加一条规则
2、读取nginx目录下的nginx.conf文件,修改两处:server_name、root
3、使用ie打开www.test.com
第一步还好解决,部分代码如下:
window.fso = new ActiveXObject("Scripting.FileSystemObject");
//假定hosts文件的所在目录为c盘
window.hostpath = getHostPath("c:\\windows\\system32\\drivers\\etc\\hosts");
//使用通过获取注册表信息来尝试获取hosts文件所在目录
var getHostPath = function(path) {
if (fso.FileExists(path)) {//存在于默认路径直接返回
return path;
}
var Sh = new ActiveXObject("WScript.Shell"),
key = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\",
sysroot = Sh.RegRead(key + "SystemRoot");
path = sysroot + "\\system32\\drivers\\etc\\hosts";
if (fso.FileExists(path)) {
return path;
}
try {
if (fso.FileExists(path = Sh.RegRead(key + "HostPath"))) {
return path;
}
} catch (ex) {}
return false;
}
//接下来就是获取指定文件、保存文件两个method了
var getFileText = function(url) {
var ts = fso.OpenTextFile(url, 1, true),//1只读、2写方式、8打开并从文件未必开始写
text = ts.AtEndOfStream ? "" : ts.ReadAll() ;
ts.close();
return text.trim();
}
var setFileText = function(url, text) {
try {
var ts = fso.OpenTextFile(url, 2);
ts.write(text);
ts.close();
return true;
} catch (e) {
alert("保存文件\n"+url+"\n失败!请检查文件是否为只读属性");
return false;
}
}
有了上面的思路,修改nginx.conf也就很容易了。但是我在处理nginx的停止、重启的时候遇到了问题。
我先在nginx的目录下弄了两个bat批处理文件:restart.bat、stop.bat文件,如下图所示:
stop.bat里面的内容:
nginx.exe -s stop nginx.exe -s quit
restart.bat里面的内容:
nginx.exe -s stop nginx.exe -s quit nginx.exe -s reload start nginx
之后我尝试使用下面的方法直接调用两个bat:
var wsh = new ActiveXObject("WScript.Shell");
var ret = wsh.Run(stopBat, 0, true);
//判定ret是否为0,为0则说明调用的时候出问题了
关于WScript.Shell的Run方法,可以参数msdn上的解释:http://msdn.microsoft.com/en-us/library/d5fk67ky(VS.85).aspx
但直接调用却达不到预期效果,怀疑是执行的时候路径不对,因为直接到nginx的目录下运行上面两个bat是没有问题的,想到先要使用cd切换到nginx所在的目录下。关于批处理如何获取路径的问题,可以参考这篇文章(虽然也是转的,也不知道谁是原创了)http://blog.csdn.net/kome2000/archive/2011/04/29/6372050.aspx
这里只需要做两点:
1、切换到nginx所在的盘符,不确定那个zip会在哪个盘解压出来
2、再切换到nginx所在的目录
对应的两条命令是:
cd %~d0 cd %~dp0
这样我再试的时候就OK了,达到要求了。
hta运行效果如下:
但仍需要注意的问题:
1、解压的zip文件不能放在中文目录下,因为nginx会启不来;
2、nginx.conf的默认配置需要事先开启对ssi的支持,如果不开启,打开游戏官网凡有include的地方就空白了,只需要加二行就OK了
ssi on; ssi_silent_errors on;
3、如果装有一些防护软件的机器上,在运行hta文件时需要允许它运行,不然没得完了。
到此为止,想法才算完整的实现了:只需要点击按钮就可以在本地预览官网,无论你将它放在磁盘哪个地方(除了不能放在中文目录下)。
总结一下,用到的技术:php、javascript、css、hta、batch(批处理)、nginx的配置、SSI,上面记录下的是在实现那个想法时遇到的一些问题,以及如何定位问题然后解决它的。
完整的hta代码:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>本地运行游戏官网</title>
<style type='text/css'>
h3 {border-left:3px solid 666; color:#666; text-indent:10px; margin:15px 0;}
textarea {border:2px solid #888; background-color:#222; font-family:'Courier New'; font-size:14px; color:#3c0;}
a {color:#657528;}
a:hover {background:#c8dc7b;}
.exec-btn {display:block; border:1px solid #666; background-color:#9c0; color:#360; padding:5px 5px 3px 5px; margin:50px; width:70px; height:30px; vertical-align:middle; cursor:pointer; display:inline-block;}
#info_list li {margin-top:10px;}
</style>
</head>
<body style='background-color:#eee; margin:0; padding:0; overflow:hidden;'>
<button class="exec-btn">执行操作</button>
<button class="exec-btn">关闭nginx</button>
<button class="exec-btn">访问官网</button>
<ul id="info_list"></ul>
/*
* 提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释
* <script type="text/javascript"> 1: 2: String.prototype.trim = function(r){ 3: return this.replace(r || /(^\s+)|(\s+$)/g, ""); 4: } 5: 6: !(function() { 7: var getElem = function(id) { 8: return typeof id === 'string' ? document.getElementById(id) : id; 9: } 10: 11: var addMessage = function(msg) { 12: var ele = getElem("info_list"); 13: var li = document.createElement("li"); 14: li.innerHTML = msg; 15: 16: ele.appendChild(li); 17: } 18: 19: var messageConfig = { 20: 1 : '开始配置nginx.conf', 21: 2 : '完成hosts文件的配置', 22: 3 : '正在启动浏览器', 23: 4 : '开始配置hosts文件', 24: 5 : '完成对配置nginx.conf', 25: 6 : '正在停止nginx应用程序', 26: 7 : '停止nginx应用程序失败', 27: 8 : '停止nginx应用程序成功', 28: 9 : '正在退出nginx', 29: 10 : 'nginx退出失败', 30: 11 : 'nginx退出成功', 31: 12 : '正在启动nginx应用程序', 32: 13 : 'nginx启动失败', 33: 14 : 'nginx启动成功', 34: 15 : '访问网站主页' 35: }; 36: 37: var WEB_SITE_URL = "http://www.test.com/"; 38: var SERVER_NAME = WEB_SITE_URL.replace(/^http\:\/\//, "").replace(/\/$/, ""); 39: var HOST_RULE_TEXT = "127.0.0.1\t"+SERVER_NAME; 40: 41: var getHostPath = function(path) { 42: if (fso.FileExists(path)) return path; 43: var Sh = new ActiveXObject("WScript.Shell"), 44: key = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\", 45: sysroot = Sh.RegRead(key + "SystemRoot"); 46: path = sysroot + "\\system32\\drivers\\etc\\hosts"; 47: 48: if (fso.FileExists(path)) { 49: return path; 50: } 51: 52: try { 53: if (fso.FileExists(path = Sh.RegRead(key + "HostPath"))) { 54: return path; 55: } 56: } catch (ex) {} 57: 58: return false; 59: } 60: 61: var getFileText = function(url) { 62: var ts = fso.OpenTextFile(url, 1, true),//1只读、2写方式、8打开并从文件未必开始写 63: text = ts.AtEndOfStream ? "" : ts.ReadAll() ; 64: 65: ts.close(); 66: 67: return text.trim(); 68: } 69: 70: var setFileText = function(url, text) { 71: try { 72: var ts = fso.OpenTextFile(url, 2); 73: ts.write(text); 74: ts.close(); 75: return true; 76: } catch (e) { 77: alert("保存文件\n"+url+"\n失败!请检查文件是否为只读属性"); 78: return false; 79: } 80: } 81: 82: var getApplicationPath = function() { 83: var url = document.location.href; 84: url = url.replace(/[^\.|/]+.hta/, "").replace(/file\:[^\w]+/i, ""); 85: 86: return url; 87: } 88: 89: var getNginxPath = function() { 90: return getApplicationPath() + "nginx-1.0.2"; 91: } 92: 93: var configHostsCallback = function() { 94: addMessage(messageConfig['2']); 95: 96: var path = getNginxPath(); 97: try { 98: fso.GetFolder(path); 99: } catch (ex) { 100: alert("nginx-1.0.2文件夹在当前目录下未找到,请检查配置!"); 101: return false; 102: } 103: 104: addMessage(messageConfig['1']); 105: var configURL = path + "/conf/nginx.conf"; 106: var configText = getFileText(configURL); 107: var arr = configText.split("\n"); 108: for (var i = 0, len = arr.length; i < len; i++) { 109: if (/^\s+server_name\s+([\w\.\d]+);\s*$/.test(arr[i])) { 110: arr[i] = arr[i].replace(RegExp['$1'], SERVER_NAME); 111: continue; 112: } 113: 114: if (/^\s*root\s+([^;]+);\s*$/.test(arr[i])) { 115: arr[i] = arr[i].replace(RegExp['$1'], getApplicationPath()); 116: break; 117: } 118: } 119: if (arr.join("\n") !== configText) { 120: setFileText(configURL, arr.join("\n")); 121: } 122: addMessage(messageConfig['2']); 123: 124: var wsh = null; 125: 126: try{ 127: wsh = new ActiveXObject("WScript.Shell"); 128: }catch(e){ 129: alert(e.message+" 创建脚本宿主对象失败。") 130: return; 131: } 132: 133: var stopBat = path + "/stop.bat", 134: restartBat = path + "/restart.bat"; 135: 136: addMessage(messageConfig['6']); 137: if (wsh.Run(stopBat, 0, true) == 0) { 138: addMessage(messageConfig['7']); 139: } else { 140: addMessage(messageConfig['8']); 141: } 142: 143: addMessage(messageConfig['12']); 144: if (wsh.Run(restartBat, 0, true) == 0) { 145: addMessage(messageConfig['13']); 146: } else { 147: addMessage(messageConfig['14']); 148: } 149: addMessage(messageConfig['15']); 150: openMainPage(); 151: 152: document.getElementsByTagName("button")[0].setAttribute("disabled", false); 153: } 154: 155: var updateHosts = function() { 156: if (!hostpath) { 157: alert('无法获取你本机的HOSTS文件,程序将中止继续运行!'); 158: return false; 159: } 160: 161: var hostText = getFileText(hostpath); 162: var arr = hostText.split('\n'); 163: 164: for (var i = 0, len = arr.length; i < len; i++) { 165: if (arr[i].indexOf(HOST_RULE_TEXT) > -1) { 166: addMessage("hosts文件中已存在规则“"+HOST_RULE_TEXT+"”"); 167: configHostsCallback(); 168: return ; 169: } 170: } 171: 172: addMessage(messageConfig['4']); 173: arr.push("\n\n#本地运行官网\n" + HOST_RULE_TEXT); 174: 175: if (arr.join("\n") !== hostText) { 176: setFileText(hostpath, arr.join("\n")); 177: } 178: 179: configHostsCallback(); 180: } 181: 182: var openMainPage = function() { 183: var Sh = new ActiveXObject("WScript.Shell"); 184: Sh.run('iexplore -new ' + WEB_SITE_URL); 185: } 186: 187: document.getElementsByTagName("button")[0].onclick = function() { 188: this.setAttribute("disabled", true); 189: 190: getElem("info_list").innerHTML = ""; 191: 192: updateHosts(); 193: } 194: 195: document.getElementsByTagName("button")[1].onclick = function() { 196: this.setAttribute("disabled", true); 197: 198: var path = getNginxPath(); 199: try { 200: fso.GetFolder(path); 201: } catch (ex) { 202: alert("nginx-1.0.2文件夹在当前目录下未找到,请检查配置!"); 203: return false; 204: } 205: 206: var wsh = null; 207: 208: try{ 209: wsh = new ActiveXObject("WScript.Shell"); 210: }catch(e){ 211: alert(e.message+" 创建脚本宿主对象失败。") 212: return; 213: } 214: 215: var stopBat = path + "/stop.bat"; 216: 217: if (wsh.Run(stopBat, 0, true) == 0) { 218: alert("执行关闭nginx失败"); 219: } else { 220: alert("操作成功!"); 221: } 222: 223: this.setAttribute("disabled", false); 224: } 225: 226: document.getElementsByTagName("button")[2].onclick = openMainPage; 227: 228: window.fso = new ActiveXObject("Scripting.FileSystemObject"); 229: window.hostpath = getHostPath("c:\\windows\\system32\\drivers\\etc\\hosts"); 230: })();</script>
*/
</body>
</html>