压缩软件

大概99年的时候,家里有了电脑。那时候机器贵硬盘空间小,虽然8000块钱的机器,硬盘只有5.1G。宽带还没普及,经常要用到1.44M的软盘。压缩软件自然居家必备。Windows 98的时代,Windows还原生不支持zip,需要Winzip来搞定。可是Winzip只是共享软件,非免费软件,过了试用期都会提示你付费,只好到处找破解版。后来Winrar不知为何取代了Winzip,占领了用户的桌面。(后文有解答)
这几天导师让我做个课程主页,里面要带上Slide。可是Office转换而成的HTML效果总是不尽如人意,而且一般总是IE only。还好有个网站叫Cometdocs,支持PDF->HTML。我先用PDF Creator打印成PDF,再上传到网站转换HTML。
转好了发现体积太大了。原本2M的Slide转成HTML居然涨到了10M。这也不能怪别人,HTML和PDF很不一样,背景图一张一张分开放,Slide一多就不行。正愁怎么发给老板,想到了7z的压缩比比zip好,于是就压缩成7z的格式试试。
一试吓一跳。25M的网站,压缩成zip剩下23M多,换成7z只剩下4.5M!!!原来7z支持文件间的对比压缩,而这些幻灯片的背景图大同小异,自然得到很理想的压缩比。不过文件间压缩的缺点在于,一旦只想解压缩一部分文件的时候,只能解压缩所有的文件之后再把目标文件给抽出来。
在搜索7z文件间压缩的时候,不经意看到善用佳软的两篇推荐7zip的文章,在这里这里。其中提到的传奇文章《压缩大战真相》,揭露了当初RAR PK掉ZIP的一些故事。
最后再给7-zip做做广告。全免费(个人可能无所谓用破解,但公司可承担不起这个法律风险),支持格式丰富,压缩比高,使用简单。用惯了winrar也不用怕,7zip也支持rar的解压缩,别人发给你的rar照样能打开,基本没有软件切换的成本。
想起来俺家小白也在我的唆使下用起了7-zip,挺好使的,是吧?

IE6设置Image.src的bug

这个问题我现在也没彻底搞懂。网站有一块需要建立一个Image对象,设置好src属性,onerror和onload属性,然后把Image添加为div的子元素,交给浏览器去请求图片并渲染(请看p.s.)。用其他浏览器都很正常,但用IE6浏览器访问,虽然能得到图片,但服务器的日志里会报错,第一个Exception是Connection reset by peer,第二个是多次调用response.getOutputStream()或getJspWriter()。
上了调试器,发现一次图片的调用,服务器会在入口函数的断点停留两次,说明浏览器发出了两次请求。用Fiddler看各次请求的内容,居然再也无法重现错误了。换用HttpWatch,终于重现了错误。每次图片的调用都会有两个请求,第一个请求发出后马上被掐掉(Aborted),然后接着发出第二个请求。这解释了为什么会抛Connection reset by peer的异常。第二个异常的原因,估计是当浏览器的reset请求到达服务器时,服务器的代码已经完成了生成图片的工作,并把图片塞进了response,这个时候抛来一个异常,被forward到error.jsp,于是乎二次调用了getJspWriter,报错。
网上搜了搜这个问题,有少许提到这个bug。我的解决方法很简单,就是在image append到div之后,再设置src属性。经测试解决了IE6的Aborted问题,其他浏览器,如IE7,FF3和Chrome都没什么影响。
p.s. 后来看到玉伯的博客,发现Image对象在设置了src对象以后就马上请求图片,并不等到被添加到DOM上再请求,因为Image本来就是用来做图像预加载的。所以原本的代码就有一些错误,应该先设置onerror和onload属性后,再设置src才有意义。
p.s.2 这里是一篇描述了IE中Aborted的文章,推翻了不少本文的无妄猜测。
p.s.3 经我第三次的测试,发现IE6中如果先设置image.src在appendChild,会出现Aborted问题,相反顺序则没有问题。

简单比较Apache+Tomcat的mod_jk和mod_proxy方法

这几天服务上线。由于80端口还需要blog和wiki两个服务,所以把80给了Apache管理,然后再通过apache连接后面8080的Tomcat。
Apache+Tomcat主要有三种办法实现: mod_jk, mod_proxy和ajp_proxy。mod_jk是比较专门针对Tomcat的方法,通过AJP协议连接Tomcat,mod_proxy不止可以连接Tomcat,只要是HTTP应用都可以进行反向代理。ajp_proxy不是很清楚,具体参见这里
我前后碰到过三种不同的配置环境。第一次是搭建试验服务的时候,服务搭建在一台内网机器上,通过外网机器的8080端口映射内网的80端口进行访问。由于可对外暴露的端口只有被映射的一个80端口,自然把Apache推了上去。使用mod_proxy进行反向代理配置。后来上面要求搭建两个运行实例,于是开了两份Tomcat,配了两份的proxy。一个在18080,一个在28080。

ProxyPass /app http://localhost:18080/app
ProxyPassReverse /app http://localhost:18080/app
ProxyPass /app2 http://localhost:28080/app2
ProxyPassReverse /app2 http://localhost:28080/app2
第二次的环境并不是Apache+Tomcat,而是IIS+Tomcat。对IIS的不熟悉让我一段时间之内挠破了头。IIS上我没有找到比较实用的mod_proxy组件,特别是使用ISAPI的mod_proxy。而另外一个重大变化在需求上,这次要求服务支持SaaS,而我们服务的SaaS通过访问的域名来区别不同的租户。比如通过abc.example.com和def.example.com访问的用户,虽然使用的是同一个应用,但是却在不同的租户空间内进行操作。
可行的办法是使用ISAPI Rewrite。ISAPI Rewrite事实上是提供Proxy功能的,但那是收费版本。免费的Lite版本只有URL Rewrite的功能。我只好彻底放弃了Proxy的打算,对用户做重定向。比如访问http://abc.example.com的用户将被重定向到http://abc.exmaple.com:8080/app。对于wiki和blog这些访问,在配置文件中当作例外处理。以下是配置文件:
RewriteCond %{HTTP:Host} example\.com$
RewriteCond %{HTTP:Host} !www\.example\.com$
RewriteCond %{HTTP:Host} !wiki\.example\.com$
RewriteCond %{HTTP:Host} !blog\.example\.com$
RewriteRule app/(.*) http://%{HTTP:Host}\:8080/app/$1 [NC,R=301]
RewriteCond %{HTTP:Host} example\.com$
RewriteCond %{HTTP:Host} !www\.example\.com$
RewriteCond %{HTTP:Host} !wiki\.example\.com$
RewriteCond %{HTTP:Host} !blog\.example\.com$
RewriteRule (.*) http://%{HTTP:Host}\:8080/app [NC,R=301]
RewriteCond %{HTTP:Host} www\.example\.com$
RewriteRule ^/$ http://%{HTTP:Host}/app/index.htm [NC,R=301]
RewriteCond %{HTTP:Host} wiki\.example\.com$
RewriteRule ^/(.*)$ http://www.example.com/wiki/$1
RewriteCond %{HTTP:Host} blog\.example\.com$
RewriteRule ^/(.*)$ http://www.example.com/blog/$1
第三次,经过争取,服务器的80端口让位于Apache。当我打算切回第一次的mod_proxy配置时,却发现一个致命的问题:Request里Host的内容已经被mod_proxy修改成了配置文件里的localhost,没有反映用户真实请求里的abc.example.com或者def.example.com。况且系统还要求支持实时添加租户,即在不重启系统的情况下增加新的*.example.com的访问。
mod_proxy虽然能对后台任何的HTTP服务做反向代理,却把泛域名访问给挡在了门边。另外我也终于明白了为什么试验服务器上的Tomcat访问日志里,来源都是127.0.0.1——因为来源都是Apache这个代理。
mod_jk的配置里,虽然仍然需要输入Tomcat的Host,但是似乎AJP协议的保留了前台访问的域名(后台程序通过request.getServerName()获得)。而客户端的IP地址也被成功的记录下来。有兴趣的同学们可以参见AJP 1.3的协议参考。mod_jk的配置就不贴出来了,具体可以看开头介绍proxy_ajp的那个链接