跳到正文
网络寻租
返回

程序如何作在线更新

问题

image

我们的客户端程序庞大, 笨重, 更为严重的是, 当我们的程序发布给用户之后, 如果没有问题还好, 遇到了bug, 修改很成问题. 如何把更新后的程序发布给用户呢? 很多项目在做架构的时候, 选择采用web方式来绕过这样的问题. 但是很多时候还是非用客户端程序不可. 如何让客户端程序实现在线更新功能? 这里提供我的一个非常简单的解决方案.

思路

更新的业务逻辑很简单. 只需要下面几步:

然后是具体实现上面的逻辑.

要点

更新程序解决方案

比如旧程序在dir_a目录下面, 可执行文件是a.exe. 星号括起来的是当前正在执行的文件.

dir_a: **a.exe**

我们把dir_a拷贝出一份, 到dir_backup, 最新的程序保存到dir_backup里面, 保存为new.tar.gz. 下载完毕后, 我们在dir_b里面建立一个文件, 叫need_update, 作为标识.

dir_a: **a.exe**
dir_backup: a.exe, new.tar.gz, need_update

用 execl (c/c++/python/都可以用类似的函数调用), 关闭当前程序, 跳到dir_backup环境里面, 执行dir_backup/a.exe.

dir_a: a.exe
dir_backup: **a.exe**, new.tar.gz, need_update

dir_backup/a.exe启动的时候检查当前目录下面是否有need_update, 有的话, 就解压new.tar.gz, 覆盖dir_a. 这个时候程序已经更新完成了.

dir_a: new.exe
dir_backup: **a.exe**, new.tar.gz, need_update

然后再用 `execl`, 放弃当前程序, 执行dir_a/new.exe.

dir_a: **new.exe**
dir_backup: a.exe, new.tar.gz, need_update

步骤虽然有点复杂, 但是顶用, 也不需要另外加一个更新程序. 对于python发布出去的代码来说, 一个可执行文件就上M了, 少一个是一个.

下面是实际代码, 因为是从实际项目中挖出来的, 保证不能用. 都是update.py这一个源文件的:

from qtlib import *
from qtlib.download import download
from config import add_config, conf
from tools.progresser import Progresser

import tarfile
from distutils.dir_util import copy_tree
SERVER = "http://project.googlecode.com/files/"

def update(program, version):
    """具体更新的方法"""
    if not confirm(None, '开始准备更新, 是否继续?'):
        return

    # 检查是否有更新
    if not check_update(program, version):
        return

    # 复制program
    temp_path = '../%s.backup' % program
    shutil.rmtree(temp_path, True)
    shutil.copytree(os.path.join('..', program),
                    temp_path)

    # 下载程序
    tar_file = program + '.tar.gz'
    local_tar_file = os.path.join(temp_path, tar_file)

    with Progresser("下载中, 比较忙, 你可以离开位置休息一下..","退出",0,100, None) as p:
        result = download(SERVER + tar_file,
                          local_tar_file,
                          stepper = p)
        if result == False:
            showMsg("无法连接到服务器, 请检查是否连上外部网络!")
            return
        elif result == None:
            showMsg('取消下载, 不好意思, 下次还得从头开始下载..')
            return

    showMsg('下载完成, 现在安装新程序.')
    os.chdir(temp_path)
    # 解压
    unzip(tar_file)
    # 标示一下是新程序
    with open('need_update', 'w+') as f:
        f.write(program)
    # 执行新程序
    program_name = program+'.exe'
    os.execl(program_name, program_name)

def check_update(program, version):
    """检查是否有更新"""
    # 下载versions文件
    if download(SERVER+'versions', 'temp/versions') == False:
        showMsg("无法连接到服务器, 请检查是否连上外部网络!")
        return False

    # 读取里面的信息
    data = open('temp/versions').read()
    # 格式是: program + 空格 + 版本号
    for line in data.split('\n'):
        if line.startswith(program):
            new_version = line.split(' ')[1]
            # 把版本号当作浮点来检查
            if float(version) < float(new_version):
                return True
            else:
                showMsg("程序已经是最新版本: %s, %s" % (program, version))


def check_finish_update():
    # 检查当前是否是需要更新的临时文件
    if not os.path.isfile('need_update'):
        return
    # 读取程序名称
    with open('need_update') as f:
        program = f.read()
    os.remove('need_update')
    # 然后把新程序复制回去
    old_path = '../%s'%program
    copy_tree(program, old_path)
    # 执行程序
    os.chdir(old_path)
    program_name = program + '.exe'
    os.execl(program_name, program_name)

# 加载update.py模块的时候, 判断当前是否为在backup过程中执行的..
check_finish_update()

其他

基于上面的更新逻辑, 我们还可以根据需求, 补充一些功能:

如果有人觉得这样的更新脚本有价值的话, 可以联系我, 让我整理出一份不依赖其他模块的代码出来.


分享这篇文章:

上一篇
pyside介绍
下一篇
sphinx介绍

评论

评论通过 GitHub 账号发布,需要先使用 GitHub 登录。