WordPress 数据库 导出 以及 域名 转换 的 注意事项

今天试着把 wordpress blog 的数据库导入到本地 localhost,先后历经了 首页无法显示,数据库乱码,管理界面自动跳转到公网地址,以及首页变白板四重天。。。速记在这里,望苦主共勉之。。。

  • 在目标网站(指的是要导入还原数据的 wordpress 站点,比如 localhost )上用 phpmyadmin 建新数据库,并在 Operations Tab 里面把 Collection 选项设置为 utf8_general_ci

上面两条解决乱码,接下来配置 wordpress:

  • 还是到目标网站上用 phpmyadmin,找到 wp_options (或者你自己指定的 table_prefix_options) 表,浏览里面的 column, siteurl 换成新站的地址 ,比如 http://localhost/wordpress
  • 然后 直接敲地址进入 wordpress admin 界面, Settings -> General 里把 Blog address (URL) 也换成新站地址

上面两条解决地址乱跳,然后:

  • 到 admin 界面里的 Appearance -> Themes激活一下默认主题 ,这招解决了 首页变白无法显示 的问题。用自定义主题的后果。。。

剩下语言,插件,主题,上传文件,图片,还有一些文章中的 site url 比较好改,这里不说了,或者有需要联系我, 1000rmb/hour :mrgreen:

[Workaround] AIR randomly crash on Mac

这几天很郁闷, AIR 会时不时的罢工:

AIR Crash

这个错误属于 AIR 内部 crash,没有任何 actionscript error 抛出来,我一句一句 trace 最后发现发生在 sqlite 的 sql 语句调用。但。。。它又不一定每次都会 crash。。。。

基本上如果最后的产品在 Mac 上跑然后撞到这个问题就会很欢乐。。。

不幸的是,crash 的 workaround 又被我我无情的发现了。

写轮眼

根本原因是 AIR 在 de-serialize 数据库中的 column 数据时,如果多个 column 都是复杂对象,而且对象的 ref 又纠结在一起的时候就会 boom。

解决办法就是在写入 complex object 的时候先手动 serialize 到

1
ByteArray

里,然后再塞给 db,取数据的时候再手动把

1
ByteArray

de-serialize 还原成 complex object。

详细的 crash reproduce 以及 workaround 实例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="init()">
    <mx:Script>
        <![CDATA[
            private namespace safe;        
            private namespace normal;
           
            private function init():void
            {
                use namespace safe;
                //use namespace normal; //如果把 namespace 换成 normal 在 mac 上会造成 crash
               
                var dbFile:File = new File("app-storage:/test.db");
                if(dbFile.exists)
                {
                    dbFile.deleteFile();
                }
                var connection:SQLConnection = new SQLConnection();
                connection.open(dbFile);
               
                connection.begin();
                executeSql(connection,"CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, bar0 OBJECT NOT NULL, bar1 OBJECT NOT NULL);");
                connection.commit();
               
                //喂点复杂的 object graph 给 sqlite...
                var obj0:Object = {prop0:"blah"};
                var obj1:Object = {prop0:obj0, prop1:"test"};
                var obj2:Object = {prop0:obj1, prop2: obj0};
               
                connection.begin();
                executeSql(connection,"INSERT INTO foo (bar0, bar1) VALUES(:bar0, :bar1);",{bar0:obj1,bar1:obj2});
   
                for(var i:int=0;i<1000;i++)
                {
                    obj0.prop0=Math.random();
                    executeSql(connection,"UPDATE foo SET bar0=:bar0, bar1=:bar1;",{bar0:obj1,bar1:obj2});
                    executeSql(connection,"SELECT * FROM foo");
                }
               
                connection.commit();
               
            }

            normal function executeSql(connection:SQLConnection,str:String,param:Object=null,mapping:Class=null):SQLResult
            {
                var s:SQLStatement = new SQLStatement();
                s.sqlConnection = connection;
                s.text = str;              
                if(param!=null)
                {
                    for(var k:String in param)
                    {
                        var p:Object = param[k];
                        s.parameters[":"+k] = p;
                    }
                }              
                if(mapping!=null)
                {
                    s.itemClass = mapping;
                }
                s.execute();               
                return s.getResult();
            }
           
            safe function executeSql(connection:SQLConnection,str:String,param:Object=null,mapping:Class=null):SQLResult
            {

                var s:SQLStatement = new SQLStatement();
                s.sqlConnection = connection;
                s.text = str;
               
                if(param!=null)
                {
                    for(var k:String in param)
                    {
                        var p:Object = param[k];
                        if(!isPlainType(p))
                        {//将 复杂 Object 先 write 到 byteArray 里
                            p = objectToByteArray(p);
                        }
                        s.parameters[":"+k] = p;
                    }
                }
               
                if(mapping!=null)
                {
                    s.itemClass = mapping;
                }
                s.execute();
               
                var result:SQLResult = s.getResult();
                if(result.data!=null)
                {
                    var c:int = result.data.length;
                    for(var i:int=0;i<c;i++)
                    {
                        var row:Object = result.data[i];
                        for(var j:String in row)
                        {
                            var val:Object = row[j];
                            if(val is ByteArray)
                            {// 还原 byteArray 里面的 object
                                val = objectFromByteArray(val as ByteArray);
                                row[j]=val;
                            }
                        }
                    }
                }
                return result;
            }
           
            private function isPlainType(obj:Object):Boolean
            {
                var type:String = typeof(obj);
                switch (type)
                {
                    case "number":
                    case "string":
                    case "boolean":
                    {
                        return true;
                    }
                    case "object":
                    {
                        return (obj is Date);
                    }
                }
                return false;
            }
           
            private function objectToByteArray(obj:Object):ByteArray
            {
                var bytes:ByteArray = new ByteArray();
                bytes.writeObject(obj);
                bytes.position=0;
                return bytes;
            }
           
            private function objectFromByteArray(bytes:ByteArray):Object
            {
                return bytes.readObject();
            }
        ]]>
    </mx:Script>
</mx:WindowedApplication>

[速记]不用申请 iPhone Developer Program 也可以让程序在手机上跑的方法

前提:

  1. iPhone 已经 jailbreak, 并且 MobileInstallation 文件已被替换
  2. 开发环境是 Xcode
步骤:
  1. 生成一个 Self Signed Certificate, 假设取名为
    1
    <strong>iPhone Developer</strong>
    , 步骤看这里
  2. Xcode 菜单选 Project -> New Build Phase ->New Run Script Build Phase, 然后输入下面代码:
    1
    2
    3
    4
    5
    6
    if [ "${PLATFORM_NAME}" == "iphoneos" ]; then
    platform=/Developer/Platforms/iPhoneOS.platform
    allocate=${platform}/Developer/usr/bin/codesign_allocate
    export CODESIGN_ALLOCATE=${allocate}
    codesign -fs "<strong>iPhone Developer</strong>" ${BUILT_PRODUCTS_DIR}/${WRAPPER_NAME}
    fi
  3. 1
    Info.plist

    文件加一个属性

    1
    SignerIdentity

    值为

    1
    Apple iPhone OS Application Signing
  4. Build Device Release Target
  5. 随便找个目录, 把程序的 icon 图片重命名为
    1
    iTunesArtwork

    , 同一目录下建一个目录名称叫做

    1
    Payload
    , 将 Build 好的 Device Release 版本的 $(YourAppName).app 文件拽到 Payload 目录, 将 iTunesArtwork 和 Payload 一起打包 zip, 然后改文件名为 $(YourAppName).ipa
  6. 双击 ipa 文件, iTunes 启动, 然后程序就被无情的同步到手机上了. XD
此方法刚在 2.2 版 jb 后的固件上试过, 还很热乎, 欢迎试用…

无尽欢乐 JasperReport

上个月很磕了一下 JasperReport,再次亲身印证了 80/20 法则。。。今天抽空列出几个“看上去”都用不了多少时间就可以搞定的问题。

空白 pdf 报表

我用的是 Spring 中的 JasperReportsPdfView 来帮助整合生成 pdf,这本来应该是一个非常简单的过程:Controller 返回一个标识 JasperReportsPdfView 的 ModelAndView,然后 Spring 自动生成报表。我最开始尝试的时候这样写的 Controller:

1
2
3
4
5
protected ModelAndView handleRequestInternal(HttpServletRequest request,
            HttpServletResponse response) throws Exception
{
    return new ModelAndView("pdfReport");
}

pdfReport 对应一个 JasperReportsPdfView,并配置指向了一个不需要任何动态数据的 report.jrxml 模板。 打开浏览器访问 controller ,会返回生成后的 pdf,不过内容是空的。。。改来改去,折腾了半天,最后发现原来 即使模板不需要任何动态数据,还是要传递一个 dummy 过去,才可以 render 出结果

1
2
3
4
5
6
7
8
protected ModelAndView handleRequestInternal(HttpServletRequest request,
            HttpServletResponse response) throws Exception
{
    Map<Object> model = new HashMap<Object>();
    // Spring 配置的默认 jasper datasource 在 map 中的名称是 'datasource',这个名字可以配置。
    model.put("datasource", new JRBeanCollectionDataSource(new ArrayList<Object>()));
    return new ModelAndView("pdfReport",model);
}

文字消失

开始在 dev 环境下生成好好的 pdf,放到 product 环境下变得无法完全显示所有文字。推测是嵌入字体问题,换来换去都不行。最后想起 product 环境是 windows,而 dev 环境是 linux,digg 了许久文档,终于找出原因。原来 JasperReport 在生成 pdf 的时候 text 的 layout 是以 fontName 这个属性为基准,用 Java 的 AWT 里算出字的高度,而最终显示在 Adobe Reader 中的事根据 pdfFontName 这个属性。很显然, Linux 和 Windows 下面算出的文字高度不同。Linux下面可以显示,但是 Windows 下面高度就不够了,所以出现了缺字的现象。解决办法就是把 TextField 的高度加 4 px 左右。后来还想出一个方法是设置 stretch with overflow 那个属性,不过没有具体测试。

isStretchWithOverflow 失灵

用户输入的多行文字不能完全显示,只显示了在 iReport 里面设计时候的那部分,仿佛 isStretchWithOverflow 设置了跟没设置一样。又是昏天黑地的 digg,最后发现原因在客户端录入,Flex 的文本域 line separator 竟然是 \r 。Java 这边则是 \n,JasperReport 的文本 stretch 很可能是数文本有多少个 line separator 然后算出总高度,结果当然是算不出实际的行数。解决的办法是在把文本发到 JasperReport 之前,先 correct line separator。

总结

是福不是祸,是祸躲不过。上面的问题每一个都耗掉 2 hours+ 的时间来 debug,记在这里,希望可以造福后人。另外有一点比较奇怪的是,JasperReport 的论坛好像完全没有被 google index,出了问题一定要到 这里这里 才可以啊。囧

我佛慈悲。。。

AMF3 Integer/Number 与 GDS

AMF3 里面的 integer 类型使用 4 bytes 来传输,但是是以一种诡异的形式,摘 osflash 上的一段描述:

Integer-data is probably the single most used item in AMF3. To save space it is an integer that can be 1-4 bytes long. The first bit of the first three bytes determine if the next byte is included (1) in this integer-data or not (0). The last byte, if present, is read completely (8 bits). The first bits are then removed from the first three bytes and the remaining bits concatenated to form a big-endian integer.

这里的重点是:为了省地方, amf3 的 integer 牺牲了部分范围,只能表示 -268435456(int.MIN_VALUE»3) 到 268435455(int.MAX_VALUE»3) 范围内的数。

这引出了一个问题,如果在 java 中一个 integer 的数值大于 amf3 可允许的范围传输的时候怎么办? survey 了一些 implement 代码,发现这种时候会用范围更大的 8 bytes number 类型来传输。

[分隔线若干条...]

最近程序 (Flex + GDS + Hibernate + 其他) 被 User 用出一个神秘的 exception:

javax.persistence.PersistenceException: org.hibernate.TypeMismatchException: Provided id of the wrong type. Expected: class java.lang.Integer, got class java.lang.Double

由于无法 reproduce,我就把他当成了灵异事件,没有理会。没想到这个不着调的 exception 竟然在昨天出现在朕的面前!经过 3.765 秒的分析和 4.132 秒的 survey 终于发现了问题的根源。(深吸一口气)原来是有一个 proxy entity 的 id 超过了 amf integer 可允许的最大范围,这个从 java 传到 flex 还没什么问题,但是回传会来的时候由于 GDS 将 amf number 读成 double 所以造成 id 类型为 double,但实际上这个 id 被声明为 integer,所以在就出现了上面那个 exception (吐气…)

有趣的是,只有当 entity 是 hibernate proxy 的时候才会出现这个问题。看来是 GDS 某个地方写的有问题,在代码里游啊游,发现了这段:

1
2
3
4
    // /graniteds/hibernate/org/granite/hibernate/HibernateProxyInstanciator.java
    public void readId(ObjectInput in) throws IOException, ClassNotFoundException {
        this.id = (Serializable)in.readObject();
    }

上面的这个代码是从 svn 的 trunk 上粘下来的,看来这个问题还没改…(没被发现?)

总之,如果有谁不幸中招,可以试试 apply 这个 patch 到 /graniteds/hibernate/org/granite/hibernate/HibernateProxyInstanciator.java 。

Over

E4X 的另类应用

一直以来我们在 actionscript 中内嵌大段的多行文本时必须在每一号加上引号,然后再在结尾写加号。问题是一天三遍的写,太麻烦了。。。一直也没见过什么解决办法(除非写个外部文件载入,不过那个不爽,请忽略)。直到有一天。。。也就是昨天晚上,我一不小心想到了一个解决方案,大家请看:

1
2
3
4
5
6
7
8
9
10
11
12
13
var t:String=plain()(<!--
The Flash Player API classes are in the flash.* packages.
The Flash Player API refers to all packages, classes, functions,
properties, constants, events, and errors that are in the flash package.
They are unique to Flash Player, as opposed to the top-level classes,
such as Date, Math, and XML, or the language elements, which are based
on ECMAScript. The Flash Player API contains features that you expect
to find in object-oriented programming languages, such as the
flash.geom package for geometry classes, as well as features specific
to the needs of rich Internet applications, such as the flash.filters
package for expressiveness and the flash.net package for handling data
transmission to and from a server.
-->);

这个 plain 方法把代码中的一整段文本复给 t 变量,而字符串本身不用加引号!关键点就在 标识符,挖哈哈,这个 e4x 的应用还不错吧。

另外一个想到的应用是不用 Flex 的 embed 标签也可以内嵌图片等二进制元素到源码中,因为 Flex 的 embed 语法是不兼容 flash9 的,而且需要莫名其妙的引入 mx 包下的一些类。所以这个应用也是有一定用处地:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var b:String=plain(/W*/g)(<!--
89504e470d0a1a0a0000000d49484452
0000000e000000100803000000114abe
080000000467414d410000afc837058a
e90000001974455874536f6674776172
650041646f626520496d616765526561
647971c9653c0000000f504c5445ffff
ffff0000cccccc9999990000003952e0
8a000000314944415478da626046010c
cc0c08c0c4423a9791118261b28c8c20
1e4231988743165d2fb9ce6042022c0c
2c280020c0006dc501097dfe85550000
000049454e44ae426082
-->);

至于这一大堆乱码是什么意思,大家意会吧。。。

plain 方法的具体实现就不说了,很简单。有兴趣的朋友可以 在这里下载源码和实例 研究一蛤。

另外通过这个方法,我还发现了 as3 的编译器,以及 flex builder 2 的 as3 编辑器在词法分析上还是存在问题的。比如有时候我在 之间加入一整段类/包定义,编译器居然报错了。。。而且编辑器也很勤劳的加上语法高亮什么的。。。