Published at: 05:11 pm - Monday November 02 2009
web.py是个很小的python框架,特点就是小,连session都没有实现人家就发布了。前几天KungfuRails大会上他们吹牛说Sinatra可以写出世界上最小的Webapp,但是有web.py的化,那个最小至少要加上个“之一”。 Web.py在Lighttpd上通过fastcgi运行的配置,可以在web.py的网站上找到文档: http://webpy.org/cookbook/fastcgi-lighttpd 实际我用的时候把静态的index.html用作首页,稍改动一下: server.document-root = “/home/sun/projects/sdostatweb” server.modules += ( “mod_fastcgi” ) server.modules += ( “mod_rewrite” ) server.port = 4000 mimetype.assign = ( “.html” => “text/html” ) index-file.names = ( “index.html” ) fastcgi.server = ( “/sdostatweb.py” => (( “socket” => “/tmp/fastcgi.socket”, “bin-path” => “/home/sun/projects/sdostatweb/sdostatweb.py”, “max-procs” => 5, “bin-environment” => ( “REAL_SCRIPT_NAME” => “” ), “check-local” [...]
Published at: 09:10 pm - Thursday October 22 2009
项目的下一阶段,需要做一些数据展现方面的工作,处于对Adobe产品的抵触情绪,我个人还是倾向于用JavaScript来完成。今天找了几个画Chart的库,比较一下功能和编程接口的使用,为接下来的开发做一些准备。 dojox.charting (BSD License) 没什么悬念,第一个想到的就是曾经用过的dojo。之前实习时候用dojo画chart也算是积累了一些心得。dojox.charting的最主要优点是编程接口完善、全面,可以配置的项目很多,接口易于编程,易于动态地生成、操作Chart。而另一方面最主要的问题就是文档比较匮乏,不了解的人可能问了,doc.dojotoolkit.org好强大,怎么会文档匮乏呢。其实是dojo和mootools类似,都很喜欢用option object来传递可选参数,但是这些option object在文档中没有任何涉及,很多关键的属性都在其中但是却没有办法查到,算是美中一大不足。 <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <html xmlns=”http://www.w3.org/1999/xhtml”> <head> <script src=”dojo-release-1.3.2/dojo/dojo.js” djConfig=”isDebug: true”></script> <title>Dojo</title> <script type=”text/javascript”> dojo.require(“dojox.charting.Chart2D”); </script> </head> <body> <h1>Dojox.charting</h1> <div id=”charting” style=”width:400px; height:250px;”> </div> <script type=”text/javascript”> (function charting(){ dojo.empty(“charting”); var data = [2,4,5,7,7,4,4,7]; var chart = new dojox.charting.Chart2D(“charting”); chart.addPlot(“default”, {type: “Lines”, markers: true, shadows: [...]
Published at: 08:10 pm - Thursday October 15 2009
今天又讨论了一种验证码服务的机制,这种机制相对前两天说的简化的验证码生成的部分,由两步生成变成了一步生成,当然由于生成图片的接口直接暴露给用户,存在被刷的可能。 用户浏览器向应用服务器请求包含验证码的页面; 应用服务器将包含验证码地址的网页发送给用户浏览器; 用户浏览器通过img的src中的固定链接向验证码服务器请求验证码图片; 验证码服务器输出验证码图片流到用户浏览器,将sessionid写入cookie; 用户判读验证码图片,提交表单; 应用服务器取出cookie中的sessionid和用户输入发往验证码服务器; 验证码服务器进行验证,返回通过或拒绝; 应用服务器根据验证码服务器结果进行响应。 优点: 固定链接,简化了接入,便于接入静态页面; 缺点: 写cookie受到域的限制,只能在相同的域中使用该服务; 验证码接口暴露给用户,可能被穷举 百度和腾讯使用的都是这种方式。
Published at: 05:10 pm - Tuesday October 13 2009
某人已经发展到上班时间写blog了。 继续说验证码服务,找到两个比较典型的。recaptcha非常著名,是twitter和facebook使用的验证码服务(不知道现在的情况。。。),vidoop提供了一种很有特点的验证码机制。 两个服务都提供了python的接口封装便于接入,通过api接口可以管中窥豹,大致了解这两个验证服务的机制。 recaptcha 下面是一段结合了web.py的简单调用 public_key = “******” private_key = “********” class Recaptcha(object): def GET(self): r = recaptcha.displayhtml(public_key) return render.recapt(r) def POST(self): params = web.input() recaptcha_challenge_field, recaptcha_response_field = params.recaptcha_challenge_field, params.recaptcha_response_field remote_ip = web.ctx.ip result = recaptcha.submit(recaptcha_challenge_field, recaptcha_response_field, private_key, remote_ip) return result.is_valid 我在GET请求中获取验证码,在POST请求中提交验证码。 recaptcha返回的是一段recaptcha自己风格的html片段,效果大家参考twitter的验证码,实际上是一个iframe,iframe的url中包含了哈希串。其中的字段名也自然被写死成recaptcha_challenge_field和recaptcha_response_field,考虑到应用服务器无需验证这两个field的输入,所以也无可厚非。recaptcha_response_field用于输入字符,recaptcha_challenge_field在载入时被修改为一个唯一key。 提交验证时,recaptcha需要提供以上两个用户输入和应用的privatekey以及浏览器ip。recaptcha通过recaptcha_challenge_field 应用的private key以及用户出口ip可以唯一标示用户,并包含一些冗余实现安全相关策略。 Vidoop Vidoop提供的服务机制与recaptcha大同小异 customer_id = “***” site_id = “localtest” api_username [...]
Published at: 09:10 pm - Monday October 12 2009
方式1,应用服务器负责生成验证码字符,验证码服务器主要负责验证码图片生成。 用户浏览器向应用服务器请求包含验证码的页面; 应用服务器生成验证码字符,存储在session中;应用服务器发送相关图片参数(验证码字符、宽、高、复杂度、背景色等)到验证码服务器; 验证码服务器返回图片地址到应用服务器; 应用服务器将包含验证码地址的网页发送给用户浏览器; 用户浏览器通过img的src方式向验证码服务器请求验证码图片; 验证码服务器输出验证码图片流到用户浏览器; 用户判读验证码图片,提交表单; 应用服务器取出session中的验证码字符比对,返回结果。 这种方式的优点: 较少的HTTP请求调用 替换原应用中独立的验证码功能相对容易 验证码服务器相对简单 方式2,验证码服务器承担验证功能,应用服务器在验证中仅起到传递作用。 用户浏览器向应用服务器请求包含验证码的页面; 应用服务器发送相关图片参数(宽、高、复杂度、背景色等)到验证码服务器; 验证码服务器返回图片地址、惟一的会话id到应用服务器; 应用服务器将包含验证码地址的网页发送给用户浏览器; 用户浏览器通过img的src方式向验证码服务器请求验证码图片; 验证码服务器输出验证码图片流到用户浏览器; 用户判读验证码图片,提交表单; 应用服务器将第三步获得的会话id和用户输入的验证码字符传给验证码服务器; 验证码服务器进行验证,返回通过或拒绝; 应用服务器根据验证码服务器结果进行响应。 这种方式的优点: 验证码服务功能完善,涵盖整个验证流程; 验证码服务端有详细的验证日志记录,便于数据分析; 欢迎大家就两种方式发表意见~
Published at: 07:10 pm - Tuesday October 06 2009
针对地理数据的ORM,有一个Hibernate的扩展HibernateSpatial项目可以将JTS对象映射到MySQL/PostGIS/Oracle中。这个扩展同样可以用在Grails里,这里有一篇简单的介绍,关于在Grails和MySQL中管理地理数据: http://www.grails.org/MySQL+GIS-Geometry+with+Grails 不过按照这个文章里介绍的方法用,很可能会遭遇这样的报错: org.hibernate.MappingException: No Dialect mapping for JDBC type: 2003 这个问题最终在这里得到了解答: http://n2.nabble.com/No-Dialect-mapping-for-JDBC-type-2003-td1141106.html 按照邮件列表里的反映,上面的配置在Postgis里是可以work的,但是如果用Mysql还需要指定JPA的columnDefinition,对应的Hibernate属性是sql-type。虽然作者承诺会在今后的版本里修改这个问题,不过眼下的M2版本还没有修正这个问题。为此,Grails的用户特地提出在Grails中加入sql-type的支持: http://jira.codehaus.org/browse/GRAILS-3201 现在按照下面文档的说明,可以在mapping里指定sqlType了: http://grails.org/doc/latest/ref/Database%20Mapping/column.html 实例代码里的domain定义应该改成: import com.vividsolutions.jts.geom.Polygon import org.hibernatespatial.GeometryUserType public class MyPoly { String name Polygon poly static mapping = { poly type: GeometryUserType, sqlType:”GEOMETRY” } } 于是,再也没有莫名其妙的No Dialect报错了。
Published at: 11:10 pm - Monday October 05 2009
废话不说了,直接上code吧: import com.drew.imaging.jpeg.*; import com.drew.metadata.*; import com.drew.metadata.exif.*; file = new File(“sample.jpg”); meta = JpegMetadataReader.readMetadata(file); gpsdir = meta.getDirectory(GpsDirectory.class); lat = gpsdir.getRationalArray(GpsDirectory.TAG_GPS_LATITUDE); lon = gpsdir.getRationalArray(GpsDirectory.TAG_GPS_LONGITUDE); lats = lat[0].doubleValue() + lat[1].doubleValue()/60 + lat[2].doubleValue()/3600; lons = lon[0].doubleValue() + lon[1].doubleValue()/60 + lon[2].doubleValue()/3600; println(lats); println(lons); metadata-extractor似乎是现在惟一的读取exif信息的java库。在ivy中可以添加这样一个依赖: <dependency org="com.drewnoakes" name="metadata-extractor" rev="2.4.0-beta-1" conf="runtime"/>
Published at: 09:09 pm - Saturday September 19 2009
今天偶然看到一个Ruby的GeoHash库,功能非常简单,就是将经纬度坐标哈希成字符串,并且,利用这个字符串可以粗略地比较两点的距离。这个库的代码host在GitHub上,要安装,需要将github加入gem: sudo gem sources -a http://gems.github.com sudo gem install davotroy-geohash 用法,irb: irb(main):001:0> require ‘rubygems’ => true irb(main):002:0> require ‘geohash’ => true irb(main):003:0> GeoHash.encode(32.168, 118.54) => “wtsr12n0nj” irb(main):004:0> GeoHash.decode(‘wtsr12n0nj’) => [32.168, 118.54] 代码可以从github上获得 git clone git://github.com/davetroy/geohash.git GeoHash的核心部分用C实现,仔细看一下可以发现是一个四叉树的结构: [codesyntax lang="c"] #define BASE32 “0123456789bcdefghjkmnpqrstuvwxyz” static void encode_geohash(double latitude, double longitude, int precision, char *geohash) { int is_even=1, i=0; [...]
Published at: 11:09 pm - Sunday September 13 2009
A simple query to test rdf and sparql. [codesyntax lang="python"] import sys import rdflib from rdflib.Graph import ConjunctiveGraph g = ConjunctiveGraph() g.parse(sys.argv[1], format=”xml”) fbns = rdflib.Namespace(“http://rdf.freebase.com/ns/”) player_refs = g.query(“”"SELECT ?player WHERE { ?root fb:soccer.football_roster_position.player ?player . }”"”, initNs={‘fb’:fbns}) players = [] for player in player_refs: gp = ConjunctiveGraph() gp.parse(player[0], format=”xml”) results = gp.query(“”"SELECT ?player_name ?player_position [...]
Published at: 08:08 pm - Thursday August 27 2009
在ArchLinux上使用ActiveMQ,执行bin/activemq,报错UnknownHostException,Transport Connection无法建立,可以取到/etc/ec.conf中设置的hostname(默认myhost) 解决方法,编辑/etc/hosts,添加127.0.0.1 myhost myhost。再次启动即可。Ubuntu上hosts自动把计算机名解析到127.0.0.1,ArchLinux上需要你手动做这件事了。