断点上传程序的一点心得
2009-4-25 21:42 | by 2ndboy最近两天花时间把之前做的基于文件的断点上传 Python 程序改成了基于数据块的断点上传,粒度变小之后,上传被打断后可以接着单个文件被打断的地方继续上传,而不用再传被打断之前就已经上传过的部分。心得记录如下:
1) 普通的文件上传用的是 STOR(STORE) 命令,断点上传需要用 APPE(APPEND) 命令:
两者的格式和参数一样,APPE 也没有用来指定 offset 的地方,所以 client 自己必须知道已经上传了多少字节,然后打开文件后把文件读指针设置到相应的地方再进行上传,Server 会把收到的数据附加到指定文件的后面。
2) 不能信赖 storbinary() 回调给出的上传数据 size:
由于上传期间随时可能断线,所以我在程序里维护了一个上传队列,里面记录了每个文件已经上传的 size,这个值是通过 storbinary() 的 callback 拿到的,示例代码如下:
- def UpdateSize( total_size, delta_size ):
- total_size[0] = total_size[0] + delta_size
- ftp = ftplib.FTP( 'localhost' )
- ftp.login( 'anonymous', '1@1.com' )
- size = [0]
- file = open( sys.path[0] + os.sep + 'test.txt', 'rb' )
- ftp.storbinary( 'STOR /test.txt', file, 8192, lambda data: UpdateSize( size, len( data ) ) )
- file.close()
- print( size[0] )
上面这段示例代码有两个需要注意的地方:
a. lambda 里不能用赋值语句,所以要通过再去调用一个函数来达到计数的目的
b. Python 里 int 类型的变量不能传引用,内容一旦改变就会另建一个新的 int 变量,所以示例里用了一个 list 来保存上传的 size 而没有直接用一个 int 变量
(这里有更优雅的方法来达到同样的目的吗?路过的高人请留言:))
但是经过测试,用 storbinary() 的 callback 记录下来的已上传 size 在断线的情况下几乎都是不准确的,全部大于真正上传的数据 size。所以这个数值不是可信的,为了解决这个问题,我用了 ftplib 里的 FTP.size() 对这个值进行校正,效果不错。
顺便提一句,SIZE 命令不是标准所定义的,所以你在 RFC959 上是找不到这个命令的,但是大多数主流的 FTP Server 都支持这个命令,基本上可以放心使用。
3) 上传时处理文件要用双重 try-except:
《A Byte of Python》的 Chapter 13 当中在介绍不管有无异常发生,文件都能被关闭时用了如下一段代码:
- try:
- f = file( 'test.txt' )
- #... ...
- finally:
- f.close()
不管是不是由于 Python 2.x 跟 3.0 的语法差异导致的,总之这段代码在我的 Python 3 环境下是运行不了的,看来 try 跟 finally 块的变量作用域是不同的。倒是在《Dive Into Python》的 6.2.3 找到一段不错的实现:
- try:
- fsock = open( filename, 'rb' )
- try:
- #... ...
- finally:
- fsock.close()
- except:
- pass
貌似最近有关技术的 post 很多,所以,没有输入就没有输出:D

