since.2006  

刚测试程序,原本好好的注册用户时验证码功能报错,取$_SESSION中服务端生成的验证码值一直为空。

debug来debug去还是没发现问题,重启服务器,连日志首页都打不开了,MYSQL抛了个“Got error 28 from storage engine”的错误。

原来发现这两个问题都是磁盘没空间引起的(昨晚BT了一个10G的文件) -_-!!

PHP创建session时,会在本地磁盘写文件。MYSQL做复杂一点的查询,估计也会往临时表里写数据。

ERROR 1030 (HY000): Got error 28 from storage engine
出现此问题的原因:临时空间不够,无法执行此SQL语句

解决办法:清空/tmp目录,或者修改my.cnf中的tmpdir参数,指向具有足够空间目录

Posted by hee at 16:03 PM | Permalink | 评论(0)

做文章系统首页时经常需要截断文章,只显示文章一部分摘要内容,这样将不会导致文章过多时使首页看起来过于臃肿。

截断含html标签的文章内容时会碰到一些问题,比如截取的地方正好是img标签,这样就会使整个页面变形。

在网上找到一种解决方法http://www.hua2r.com/blog/20c3dfecb972ccb0421cd0402bfa1106.xml

本BLOG系统首页就是用这种思路来截取字符串的。

具体思路为:

  1. 用正则将html标签和文章内容分开
  2. html标签不参加字符串长度计算(如果碰到html标签,直接拼加,是真正内容时,才计算长度)
  3. 根据html标签进/出栈(Stack,先进后出)
  4. 循环第2步,3步
  5. 超过长度,跳出循环,如果栈中有html标记,将html标记补到内容后面

上面网址列出的代码片段有几个小问题,没有考虑到font标签的处理情况。下面贴出俺修改后的

function htmlSubString($content, $maxLength=300) {    
    $content = preg_split('/(<[^>]+?>)/i', $content, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);    
    $outString = '';    
    $curLength = 0;    
    $tagStack = array();    
    foreach($content as $value) {    
        if(trim($value) == '') continue;    
        $outString .= $value;    
        if (preg_match('/^([^<>]+)$/i', $value)) {    
            $curLength += strlen($value);    
            if($maxLength < $curLength) break;    
        } else if(preg_match('/<IMG([^>]+)?>/i', $value) || preg_match('/<PARAM([^>]+)?>/i', $value) || preg_match('/<!([^>]+)?>/i', $value) || preg_match('/<HR([^>]+)?>/i', $value) || preg_match('/<BR([^>]+)?>/i', $value)) {    
            continue;    
        } else if(preg_match('/<\/([^>]+?)>/i', $value, $matches)) {    
            array_pop($tagStack);    
        } else if(preg_match('/<(\w*)\s*.*/i', $value, $matches)) {    
            array_push($tagStack, $matches[1]);    
        } else {    
            break;    
        }    
    }    
   
    $patchStack = array_reverse($tagStack);    
    foreach($patchStack as $tagName) {    
        $outString .= "</$TAGNAME>";    
    }    
    return $outString;    
}  
Posted by hee at 22:03 PM | Permalink | 评论(1)

随着ajax的流行,页面中的JS代码越来越多。一般我们用JS处理页面时,都是使用window.onload等到页面加载完毕后再来处理,否则有时会出现文档未加载完毕而报的错误了。

但window.onload有一个不好的地方,它会等到整个页面(包括图片,flash等)全部加载完毕后,才会执行。一个页面中如果有很多图片,中间会有较长的延迟才会去执行我们的代码。

具体效果:http://www.ajaxeye.com/labs/domready.htm(注意页面中的两个小方块颜色,一开始默认都是红色,注意颜色的改变)

上面页面中,会在DOM树加载完毕后window.onload时分别改变两个小方块的颜色为绿色。

解决方法可以参见英文原文:
http://dean.edwards.name/weblog/2005/09/busted/
http://dean.edwards.name/weblog/2006/06/again/

解决方法就是想办法监听DOM树加载完毕的事件,等DOM树载完毕后执行我们的代码

  • Firefox、Opera9下,可以监听DOMContentLoaded事件来处理
  • IE下,可以给script标签加defer来处理

偶这个是从mootools,prototype中抠出来的IE,Firefox下测试通过

Event.addDOMReadyEvent = function($callback) {    
    var timer, fired = false;    
        
    function fireDOMReadyEvent() {    
        if (fired) {    
            return;    
        }    
        if (timer) {    
            window.clearInterval(timer);    
        }    
        fired = true;    
        $callback();    
    }    
        
    // firefox, opera, safari ...     
    if (document.addEventListener) {    
        if (window.webkit) {    
            timer = window.setInterval(function() {    
                if (/loaded|complete/.test(document.readyState)) {    
                    fireDOMReadyEvent();    
                }    
            }, 10);    
                
            Event.addEvent(window, 'load', fireDOMReadyEvent);    
        } else {    
            // firefox, opera9 使用 DOMContentLoaded    
            document.addEventListener("DOMContentLoaded", fireDOMReadyEvent, false);    
        }    
    } else {    
        // Internet Explorer中使用 defer 属性    
        var src = (window.location.protocol == 'https') ? '://0' : 'javascript:void(0)';    
        document.write('<SCRIPT id=__tbOnDOMReady src="' + src + '" defer><\/script>');    
        $('__tbOnDOMReady').onreadystatechange = function() {    
            if (this.readyState == 'complete') {    
                this.onreadystatechange = null;    
                fireDOMReadyEvent();    
            }    
        };    
    }    
}  
Posted by hee at 22:01 PM | Permalink | 评论(2)

前些天参照extjs做了一个类似的下拉列表组件,做完后发现在IE6下切换背景图片时有明显的延迟,而extjs没有类似情况。

原因是因为IE6中默认没有缓存背景图片,虽然切换时是使用同一样图片,不同位置。但是每次都会发起一次HTTP请求(虽然会返回HTTP状态304)

可以使用如下方法解决:

// 先判断一下是否是ie6    
if (isIE6) {    
    try {    
        document.execCommand("BackgroundImageCache", false, true);    
    } catch (e) {    
    }    
} 
Posted by hee at 13:01 PM | Permalink | 评论(0)
第一篇第二篇日志中都没有管openssl\openssl.cnf配置文件中的设置,使用撤销用户证书功能时需要用到。找到 CA_default 字段处
dir = d:/youpath/ca # Where everything is kept
然后到 d:/youpath/ca 下创建index.txt,certs,crl,newcerts,private,serial,crlnumber 这些文件将serial,crlnumber的内容都设为默认值01 先撤销用户证书
openssl ca -keyfile d:\path\bin\cert\ca\ca-key.pem -cert d:\path\bin\cert\ca\ca-cert.pem -revoke d:\path\bin\cert\client\client-cert.pem -config d:\path\bin\cert\openssl\openssl.cnf
生成供tomcat使用的撤销证书列表
openssl ca -gencrl -config d:\path\bin\cert\openssl\openssl.cnf -crldays 7 -out d:\path\bin\cert\server\server-cert-list.crl

修改tomcat conf/server.xml 文件,多了个 crlFile 属性

    <CONNECTOR port="8443" protocol="HTTP/1.1" scheme="https" secure="true" 
    crlFile="d:/path/bin/cert/server/server-certlist.crl" truststorePass="123456" truststoreType="JKS" 
    truststoreFile="d:/path/bin/cert/ca/ca.jks" keystorePass="123456"
    keystoreType="PKCS12" keystoreFile="d:/path/bin/cert/server/server.p12" clientAuth="true" sslProtocol="TLS" acceptCount="100"
    disableUploadTimeout="true" enableLookups="false" maxSpareThreads="75" minSpareThreads="25" maxThreads="150" 
    maxHttpHeaderSize="8192" SSLEnabled="true" />

有资料说tomcat中使用撤销证书列表功能,需要编译org.apache.tomcat.util.net.jsse.JSSE15*.java或整个tomcat-util.jar 但在tomcat6中,实际测试时,并不需要编译上述文件,直接添加crlFile到server.xml里,重启服务器即可看到效果。如果用户证书已被撤销,将提交找不到服务器,无法打开页面。

Posted by hee at 06:09 AM | Permalink | 评论(0)
上一篇日志中介绍了tomcat+openssl实现双向认证,但第6步需要用户手工将个人证书及根证书手工导入到IE中去。这里介绍一种自动导入根证书到IE中去的方法。
  • 只自动导入根证书,在原有那篇文章的基础上不需要做什么改动,直接读出根证书内容即可实现。
  • 即自动导入根证书,又要自动导入个人证书,这种情况下,创建证书请求的工作要交给IE控件完成。
微软的ICEnroll接口中提供了createPKCS10acceptPKCS7方法来创建证书,安装证书功能。在ICEnroll3接口中提供了InstallPKCS7,可以使用它来安装根证书。

只自动导入根证书。

<%    
    StringTokenizer st = null;    
    String line = null;    
   
    // ------------------------------------------------------------- 返回CA证书内容 >>>    
    String server_cert = "开始用openssl生成的根证书内容";    
    if (StringUtils.isBlank(server_cert)) {    
        throw new BusinessException("读取CA证书失败");    
    }    
   
    // 这里的这些操作,是为了生成vbscript中证书内容的变量定义    
    String pkcs7ca = "sPKCS7ca=\"\" & vbcrlf\r\n";    
    st = new StringTokenizer(server_cert, "\r\n");    
   
    while (st.hasMoreTokens()) {    
        line = st.nextToken();    
        if (line.equals("\r\n"))    
            continue;    
        pkcs7ca += "sPKCS7ca=sPKCS7ca & \"" + line + "\" & vbcrlf\r\n";    
    }    
%>    
   
<OBJECT id=XEnroll codeBase=xenroll.dll classid=clsid:127698e4-e730-4e5c-a2b1-21490a70c8a1></OBJECT>    
   
<SCRIPT type=text/vbscript>    
    ON ERROR resume next    
    <%=pkcs7ca%>    
    XEnroll.InstallPKCS7 sPKCS7ca    
    If err.number = 438 then    
        msgbox err.description & err.number    
    Elseif err.number <> 0 then    
        msgbox err.description & err.number    
    Else    
        msgbox "根证书安装成功"   
    End If    
</SCRIPT>  
Posted by hee at 07:09 AM | Permalink | 评论(0)