很早以前记的,放在草稿箱里,没有发出来,现发出来,因为是笔记,所以看起来可能比较乱。
swfupload是上传大文件、显示上传进度以及一次上传多个文件的很好选择。但由于其采用了flash做为上传的客户端,带来了以下问题:
- cookie无法和浏览器共享:如果你限制用户登录了后才能上传,那么,用户在浏览器里的cookie你要通过post的形式传过去。
- 跨域问题:若swfupload所用的swf文件和上传处理段不在同一域名,便有此跨域问题,解决倒也简单,在上传处理端加个crossdomain.xml即可。
crossdomain.xml示例:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<site-control permitted-cross-domain-policies="all"/>
<allow-access-from domain="*.swfuploadfile.com" />
<allow-http-request-headers-from domain="*" headers="*" />
</cross-domain-policy>
更让人头疼的是报2038错误(error=-200 message=#2038),这个错误实在折腾人啊。
先看看adobe官网是怎么说的:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/FileReference.html?filter_flash=cs5&filter_flashplayer=10.2&filter_air=2.6
里面讲到了ioerror出现的可能
- An input/output error occurs while the player is reading, writing, or transmitting the file.
- The SWF file tries to upload a file to a server that requires authentication (such as a user name and password). During upload, Flash Player or Adobe AIR does not provide a means for users to enter passwords. If a SWF file tries to upload a file to a server that requires authentication, the upload fails.
- The SWF file tries to download a file from a server that requires authentication, within the stand-alone or external player. During download, the stand-alone and external players do not provide a means for users to enter passwords. If a SWF file in these players tries to download a file from a server that requires authentication, the download fails. File download can succeed only in the ActiveX control, browser plug-in players, and the Adobe AIR runtime.
- The value passed to the
urlparameter in theupload()method contains an invalid protocol. Valid protocols are HTTP and HTTPS.
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/runtimeErrors.html
- 2038 File I/O Error.This error occurs when an application can’t get file size, creation date or modification data using the FileReference API.
大概意思就是没有办法获得文件信息,另外一方面是由于上传接口需要http验证,无法输入用户名和密码无法所致。
那么先检查一下上传接口是否有上述问题(使用最原始的上传方式检查),如果上传接口正常,那么需要想想是不是客户端的问题了。有用户说杀毒软件会禁止flash上传,这个我没有实际的验证,但的确遇到过在同一swfupload系统,在有些客户端报2038错,而在其他的地方正常。
若所有的客户端都出现此问题,那么还是有可能上传接口的问题,我就碰到一例:
情况如下:
- 上传接口前面有一nginx做转发,且这个nginx距离实际处理上传文件的app服务器还有一段距离。
- 上传小文件正常,上传大文件会报2038 io error。
- 若不使用nginx转发,flash直接往app服务器上传文件,大文件也正常上传。
去检查nginx的log
cat nginx.log | grep Flash | awk ‘$12 == 413 {print $0}’
发现nginx的响应状态是499,而非正常的200。
怀疑nginx转发有问题,在客户端上传的同时,在nginx服务器上抓包看看:
sudo tcpdump -vvv -n -A host upload-app and port 80
发现nginx转发的request的post数据不全,同时upload-app服务器上给出的响应是 400 bad request。
至此,初步将原因定为flash player主动断开连接,因为本地的nginx log里面是499,关于499,http协议rfc2616中没有定义,通过相关资料查询可得,499为客户端(flash player)主动断开连接。关于nginx的499可以参考以下文章:
为什么flash会主动断开连接?我也不知。
总要写点什么吧?不为别的,为了2012-12-21前多写写文章,多给这个星球留下些琐碎。
还记得大学时,高崇伊老师跟我们讲,有人问大物理学家费曼,如果世界真的毁灭了,只能留给后人一句话,这句话是什么。费曼说,世界是由原子构成的。
假设2012-12-21真的是世界末日,你会愿意像这样生活、工作吗?会对现在做的一切都是满意吗?2011年,当我每天都对这些说no时,我再也无法忍受,我选择了重新开始。
写写过去一年的总结?新的一年的展望?
总结就罢了,过去的一年都在审视自己的现状,审视自己过去做过的事情,再怎么总结也挤不出什么味道。
展望也很简单,生活上希望有所改变;工作上面,能够把目前手上负责的几个项目做好,能够带出一个好的团队来。过去的一年真的没有什么进步。
听着王力宏的《唯一》,仿佛时间回到了十年前的傍晚,我坐在教室后面上自习,当时学校广播里放的也是这个歌。只是,这歌还是这歌,时间逝去,再已无法追回。大学的论坛很多时候都不敢上去,每每上去,看着熟悉又模糊的照片和文字,都会偷偷的把眼泪往肚子里咽;不是失望,也不是激动,而是对那段已经逝去的青涩,心里有诸多不舍和不甘。想念当时军训完用帽子舀花香的情形,虽然当时兵败2001高考,但幸运有一个基地班的过渡,我没有继续颓废下去。
有时候真的不知道从何说起,人真的是复杂的动物。罢矣,收拾心情,明天继续上班。
一般情况下,我们使用一个dns服务器就能达到我们的需求,但有时候我们需要针对不同域名,使用不同的dns,如以下场景:
- 需要dns服务器A去解释某国外域名
- 需要dns服务器B去解释局域网内部的域名
- 默认使用dns服务器C
通用的解决方法是使用自己搭建的dns forwarder服务器,然后针对不同的zone设置不同的dns服务器,bind和dnsmasq都可以做这种dns forwarder的工作。
但如果你是在mac中,则有更加简单的方法:
- 在 /etc/ 目录下建立 resolver 文件夹,即 /etc/resolver 目录
- 在/etc/resolver 目录下,针对不同的域名zone建立不同的解析文件;如我要对zwsun.com使用192.168.1.1的dns服务器,则建立文件zwsun.com,内容为: nameserver 192.168.1.1
- ping zwsun.com看看,证实一下是否设置成功
上述操作需要在root下,另外,需要说明的 是,nslookup的结果不会受这个文件的影响,但对ping程序是会生效的。
一般的策略是将大部分域名解析的dns设置在 /etc/resolv.conf中,也就是通过网络偏好设置,特殊解析需求的可以在/etc/resolver目录下建立对应的文件。
下面的讨论基于virtualbox(我选择virtualbox的原因是它免费,且支持各个平台),虚拟机为linux,宿主操作系统不限。
经常一开始建了一个固定大小的虚拟硬盘用来安装虚拟机,但过段时间数据越来越多,最终发现其空间不够用,这时候就很郁闷了,但如果不是你系统的目录不够用的话,下面的办法可以解决空间不够的问题:
1 增加一个虚拟硬盘的方法
优点:虚拟机中读写速度较快,虚拟硬盘也可以后面给其他的虚拟机使用
缺点:宿主机器无法读写块虚拟硬盘中的内容
操作方法:
1.1 先给虚拟机配备一个动态大小的虚拟硬盘
这个和一般的装虚拟机新建虚拟硬盘一样的操作。
打开虚拟机的配置 > storage 增加SATA控制器,然后在SATA控制器下增加一块虚拟硬盘;或者直接在IDE controller下面新增一块虚拟硬盘。
1.2 在虚拟机中进行分区,格式化,然后挂载
1.2.1 分区:
PS:必须要在root下操作,下面的描述中,橙色字体的为输入的命令。
先用fdisk -l看看,如果没有找到你的虚拟硬盘,那就是前面的虚拟硬盘没有配置对。
root@dev-desktop:/home/dev# fdisk -l
Disk /dev/sda: 8589 MB, 8589934592 bytes255 heads, 63 sectors/track, 1044 cylindersUnits = cylinders of 16065 * 512 = 8225280 bytesDisk identifier: 0×00015130
Device Boot Start End Blocks Id System/dev/sda1 * 1 993 7976241 83 Linux/dev/sda2 994 1044 409657+ 5 Extended/dev/sda5 994 1044 409626 82 Linux swap / Solaris
Disk /dev/sdb: 2845 MB, 2845835264 bytes255 heads, 63 sectors/track, 345 cylindersUnits = cylinders of 16065 * 512 = 8225280 bytesDisk identifier: 0×00000000
Disk /dev/sdb doesn’t contain a valid partition table
可以看到第二块虚拟硬盘 /dev/sdb 上面没有任何分区信息,当然无法用了。
接着 fdisk /dev/sdb 在第二块虚拟硬盘上建立分区,关于fdisk的详细用法请在进入fdisk后按m或者搜索之。
root@dev-desktop:/home/dev# fdisk /dev/sdb
Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabelBuilding a new DOS disklabel with disk identifier 0xadff2d79.Changes will remain in memory only, until you decide to write them.After that, of course, the previous content won’t be recoverable.
Warning: invalid flag 0×0000 of partition table 4 will be corrected by w(rite)
Command (m for help): n
Command action
e extended
p primary partition (1-4)p
Partition number (1-4): 1
First cylinder (1-345, default 1): 1
Last cylinder or +size or +sizeM or +sizeK (1-345, default 345):
Using default value 345
Command (m for help): w
The partition table has been altered!
1.2.2 格式化
有分区了,还是无法使用的,这时候需要格式化,linux下普遍使用的是ext3。我们通过mkfs对刚才的分区格式化:
root@dev-desktop:/home/dev# mkfs.ext3 /dev/sdb1
mke2fs 1.40.8 (13-Mar-2008)Filesystem label=OS type: LinuxBlock size=4096 (log=2)Fragment size=4096 (log=2)173888 inodes, 692795 blocks34639 blocks (5.00%) reserved for the super userFirst data block=0Maximum filesystem blocks=71303168022 block groups32768 blocks per group, 32768 fragments per group7904 inodes per groupSuperblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912
Writing inode tables: done
Creating journal (16384 blocks): doneWriting superblocks and filesystem accounting information: done
This filesystem will be automatically checked every 38 mounts or180 days, whichever comes first. Use tune2fs -c or -i to override.
1.2.3 挂载
通过mkdir先建立一个挂载点,如在根目录下建立一个data目录用于挂载 mkdir /data/
root@dev-desktop:/home/dev# mount /dev/sdb1 /data/
通过df命令看看挂载情况,如果有相应的挂载信息就ok了。
root@dev-desktop:/home/dev# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 7.6G 6.1G 1.2G 85% /
varrun 352M 128K 352M 1% /var/run
varlock 352M 0 352M 0% /var/lock
udev 352M 48K 352M 1% /dev
devshm 352M 0 352M 0% /dev/shm
lrm 352M 40M 313M 12% /lib/modules/2.6.24-27-generic/volatile
/dev/sdb1 2.7G 69M 2.5G 3% /data
1.2.4 设置 /etc/fstab
做这一步的目的在于将第二块虚拟硬盘的信息写入系统分区信息表,便于开机自动挂载,否则,每次都需要手动去mount
在目前的/etc/fstab文件后面增加一行
/dev/sdb1 /data ext3 defaults 0 2
关于fstab的配置和各个参数的含义,可以自行搜索之。
2 和宿主机器通过数据空间共享的方法
优点:宿主和虚拟机都可以操作共享的文件,虚拟机内的空间完全决定于宿主机器的物理硬盘和实际分区,虚拟机开启后也可以使用该办法。
缺点:速度较慢
方法:
在虚拟机的设置 > 数据空间中设置宿主的目录和权限。
进入虚拟机后以root身份运行以下命令:
mount -t vboxsf 数据空间名称 挂载点

总结:如果你在宿主能够访问虚拟机中文件这方面没有需求,推荐使用增加虚拟硬盘的方法。
众人皆知,在mac上,flash绝对是cpu杀手,只要一开有flash的网站,那cpu马上疯狂的转,不知道是adobe的问题,还是mac的问题。
鉴于此,禁用flash是个最直接的解决方法,而且一般网站大部分flash都是广告。safari中推荐使用 clickplugins这个扩展,他可以将flash暂时屏蔽,若你需要看,点击一下即可。firefox中同样有这样的插件,名字叫做flashbock,使用方法一样,需要看的时候点一下。
关于视频网站的解决方法:在safari中可以将user agent设置为iPad,这样,大部分视频网站将会给你html5的视频,这样播放起来无压力。firefox下由于不支持html5,那都是浮云了。事实上,clickplugins这个插件会对国外网站上的视频转成html5,无需切换user agent,可惜这些网站在我朝是不存在的。
下面这些知识虽然是前端相关的,但是做为一个想深入web的人,还是非常有必要知道的。很多时候当前端跟你抱怨为什么我的js中明明声明了变量,但还报未定义的错;为什么我写一个alert调试就好了?很有可能就是下面所提到的问题。
1 js为什么不按代码中的次序执行了?
正常的html中外联script,script无法并行下载,阻塞后面其他资源的下载;且执行顺序可以得到保证(新版本浏览器有改进,可以并行下载js,执行顺序也会保证)。所以我们一般在html中写的js都没有问题,但问题是,这个会影响整个网页相关资源下载速度。
鉴于此,人们又想了很多方法来“欺骗”浏览器,以达到加速下载js和相关资源文件的目的,方法大概有以下几种:
- XHR Eval :ajax获得js内容,然后执行
- XHR Injection : ajax获得js内容,然后创建一个script的node,插入到文档dom tree中
- Script in Iframe : iframe src为js文件
- Script DOM Element :动态创建script,然后设置其src
- Script Defer : script标签加入 defer属性
- document.write Script Tag :document.write(“<script type=’text/javascript’ src=’A.js’><\/script>”);
既然可以并行下载,那么执行顺序就是一个需要确认的问题了,可惜的是,不是每种技术都能保证脚本执行的次序。具体情况可以参考下表(节选自 《Even Faster Web Sites》):

2 jquery的html函数好像有时候也不灵啊!
假设我们如此调用:
$(‘#content’).html(data);
jquery 的html函数使用同步ajax下载data中的外联js。对于inline的js,执行Script DOM Element在head节点插入一个script节点。
从上面的分析来看,data中js的执行顺序是可以保证的。但有时调用jquery.html,却仍然出现执行顺序混乱的情形,原因是外联的js是不同的域。由于ajax无法跨域,故这时候无法通过同步ajax下载来加入外联js,而是通过和内联js一样的方式:将外联的js以Script DOM Element 的方式插入到head节点。按照上面的图,执行顺序是无法在所有浏览器上保证的(ensures order)。
解决方法: 在调用jquery.html前,使用 document.write Script Tag将外联的js载入。
3 文章开头提到的问题
外联js定义了js变量,但还报undefined错:很有可能就是执行顺序不对,执行使用变量的代码时,定义的js虽然在前面,但还没有下载完呢。
alert一下,一切就是好的了:还是一个道理,定义的外联js没有下载完成,同时,你可以试试setTimeout,将你使用外联js的变量的代码晚点执行,也会是好的。
4 参考资料
下面这些资料,非常值得一读:
- 《Even Faster Web Sites》
- http://www.cnblogs.com/sanshi/archive/2011/02/28/1967367.html
- http://www.cnblogs.com/sanshi/archive/2011/03/01/1968275.html
- http://www.cnblogs.com/sanshi/archive/2011/03/02/1969224.html
1 前言
话说上回将iPad2升级至iOS5,正欢天喜地的尝试wifi同步,但可惜的是我的leopard系统里面竟然连wifi同步选项都没有!我非常、极其的生气,打了一通apple的客服,无果;上苹果的论坛一看,一大堆人反映和我一样的问题。有人实测,只有在10.6.8后才能够wifi同步,连10.6.6都不行。我恨啊。
难道真的要升级系统了吗,现在老系统的问题越来越多,主要有以下方面:
- 输入法,可恨的输入法,竟然大部分都不支持10.6.8以下的系统
- 还是输入法,10.5.8里面不能设置不同窗口使用不同输入法,实在不爽(据说10.4还是可以设置的)
- 没有mac app store,下载软件完全靠游击战
- qq等很多软件不再支持10.6.8以下
- webkit在编译有些部分时,不支持10.6以下的系统
- 刚才说的,没有办法和iOS设备wifi同步
之前没有升级,主要是偶尔工作时会用,当心开发环境什么的;现在好些了,那狠下心来升级吧,为避免后面再升级,准备现在一次升到lion算了,同时,加内存到8G。
2 升级过程
升级的过程是 10.5.8 -> 10.6.3 -> 10.6.8 -> 10.7 -> 10.7.2,也就是从leopard到snow leopard再到lion,因为leopard是无法直接升到lion的。
其中10.6.3 -> 10.6.8和10.7 -> 10.7.2算更新,去apple网站下载更新包即可;其他的嘛,要么买正版(其实也不贵,snow lepord卖¥248,lion 下载时卖$29.9),要么你懂得。
升级到10.6.3的时候,最后时刻出现了安装失败,真的是让我心都凉了;幸好重新安装后成功,估计是光驱读盘的问题吧。
硬件的升级放在了安装上10.6.8之后,原有的2G内存早已不够lion用的了。内存倒是亲民价,2条4G的三星金条连350都不到。
拆开后盖,拔出之前的内存,换上新的内存(顺便可以清理一下风扇什么的),开机一次点亮。之前网上有人说可以使用1333MHz的内存,我这里算也是再次验证了,不过它工作的频率应该还是1066MHz。
下面是我机器的更加详细的信息,可以看到,虽然是2008年后期的mb 466,但是还是可以升级到lion的。

整个升级过程还算顺利,只需开机放入光盘或者点击安装程序即可,默认情况下也是升级安装,也就是说用户的程序(包括应用程序下的程序)和文档都还在的,甚至连之前应用程序的数据都在,比如safari的浏览历史,下载记录。iTunes无法识别iPad2,不过重装一下iTunes就好了,app、音乐以及图书都还在。
3 使用感受
- 8G的内存没白加,lion使用起来还是非常顺畅的;另外,系统占的硬盘空间也比leopard少了4G左右。
- 由于从leopard切换过来的,一时无法适应手势滚动方向的改变:之前leopard中,手势滚动的是滚动条,而现在lion滚动的是内容,这也和移动设备是一致的。
- 手势程序切换以及全屏是目前使用起来比较舒服的功能。
- safari浏览时,前进后退比以前更加酷了。
- iTunes终于可以iPad2无线同步了。

10月12号,apple正式发布iOS5,现在更新,虽然算不上第一时间,但这时候稳定些。下面将相关过程和初步使用体会分享一下。
更新的过程:
- 更新iTunes至10.5
- 直接在iTunes中升级至iOS5(也可以直接下载对应的固件文件,然后通过iTunes里面options(shift in windows)+恢复完成)
直接下载固件的列表:
- iPhone 3GS (点我下载)
- iPhone 4 GSM(点我下载)
- iPhone 4 CDMA(点我下载)
- iPhone 4S(点我下载)
- iPod Touch 3(点我下载)
- iPod Touch 4(点我下载)
- iPad(点我下载)
- iPad 2 Wifi(点我下载)
- iPad 2 3G(点我下载)
- iPad 2 CDMA(点我下载)
关于更新后app都需要重新下、游戏记录都没有的问题,我这边倒是没有发现,更新后app照样在,照片也在。应该是始终用一台电脑的原因吧。总之,更新过程没有遇到什么问题,就是下载固件时间长了些。
使用感受,下面的功能是个人觉得比较好用的:
- 手势:通过手势切换应用程序,回到主屏幕,查看正在运行的程序等等;有了手势,可以少按home键了
- 更新的键盘:有输入法切换的改进,增加常用的符号输入,支持浮动、分开的布局键盘;总之,输入更加方便
- safari的更新,标签操作直接可以在safari的主界面搞定,和mac版的safari类似;另外还有read later以及阅读器的功能
- 和电脑通过wifi 同步,可惜我的Leopard系统(10.5.8)好像不支持,完全没有
- iCloud,可以同步一大堆数据
- 更好的通知中心
当然,也有些更新其实不是那么好玩了,比如报刊杂志,个人觉得是鸡肋,至于iMessage,我觉得还不好说呢。
10月4日,苹果举行了今年秋季的产品发布会。按apple之前的惯例,本应该今年早些时候发布新产品的;拖到现在,使很多投资者和消费者有了更多的期待,但总体来说还是让人比较失望的,这在苹果的股价上有很好的体现。发布会主要有以下信息:
- 没有iPhone5,只有iPhone 4 的升级版iPhone 4s,即使这样,连iPhone 4s的介绍也不是CEO Tim
- ios 5的介绍,主要是iCloud, Find my Friend 以及一个Card的程序
- iPod, iPhone产品全线降价
- 其他相关apple store, iPod, mac,iPhone, iPad, ios等数据介绍
其实我等围观之,主要是想要看看apple能够有哪些新功能的推出,但是创新也不是那么容易的,不可能每次发布会都能像iPhone, iPad刚推出来那样让人耳目一新。但个人觉得本次发布会,Siri还算是一个亮点了(Let ‘s talk iPhone 中 的talk应该是指这个意思吧),Siri虽然基础是语音识别,之前也有很多类似的语言识别程序,但之前的程序交互性不强,大多数的功能也是将语言转换成文字;而Siri则更在问答、帮忙人们完成任务这一方面更进一步。
上次和同事聊到了验证码中有表达式求值的问题,说起来应该是比较简单了,也是数据结构上经典的算法问题。可是自己写一写才会发现,做对一件看似简单的事情不是那么容易。
本来自己弄了一个c的stack,但是c没有模板,想不到怎么支持不同的类型,只好换用c++了。
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <stack>
#include <iostream>
using namespace std;
/**
* 比较运算符之间的优先级
*/
static short int Precede(char c1, char c2) {
if (c1 == c2) {
if (c1 == '#') {
return 0;
} else if(c1 == '(') {
return -1;
} else {
return 1;
}
}
switch (c1) {
case '+':
case '-':
if (c2 == '*' || c2 == '/' || c2 == '(') {
return -1;
} else {
return 1;
}
break;
case '*':
case '/':
if (c2 == '(') {
return -1;
} else {
return 1;
}
break;
case '(':
if (c2 == ')') {
return 0;
} else {
return -1;
}
break;
case ')':
return 1;
break;
case '#':
return -1;
}
}
/**
* 对加减乘除进行求值
*/
static float Operate(float a, char theta, float b) {
switch (theta) {
case '+':
return a+b;
break;
case '-':
return a-b;
break;
case '*':
return a*b;
break;
case '/':
return a/b;
break;
}
printf("bad operator");
return 0;
}
int main() {
stack<char> optr;
stack<float> opnd;
optr.push('#');
char theta, tmp[20], *tmppoint, basechar[20];
tmppoint = basechar;
float a, b;
char c = getchar();
while (c!='#' || optr.top() != '#') {
if (isdigit(c)) {
*(tmppoint++) = c;
c = getchar();
} else {
if (tmppoint > basechar) {
*tmppoint = '\0';
opnd.push(atof(basechar));
printf ("push number %s\n", basechar);
tmppoint = basechar;
}
if (c != '+' && c != '-' && c != '*' && c != '/' && c != '(' && c != ')' && c != '#' ) {
c = getchar();
continue;
}
short int precede = Precede(optr.top(), c);
switch (precede) {
case -1:
optr.push(c);
printf("pushed operator %c \n", c);
c = getchar();
break;
case 0:
optr.pop();
c = getchar();
break;
case 1:
theta = optr.top();
optr.pop() ;
if (opnd.empty()) {
printf("opnd is empty\n");
}
b = opnd.top();
opnd.pop();
a = opnd.top();
opnd.pop();
cout << " cal a=" << (float)a << " theta=" << theta << " b=" << (float)b << endl;
opnd.push(Operate(a, theta, b));
break;
}
}
}
/* 最后的结果 */
printf("%f\n", opnd.top());
printf("%c\n", optr.top());
return 0;
}

