美文网首页
怎么实现软件更新?

怎么实现软件更新?

作者: Stevent | 来源:发表于2019-04-11 14:12 被阅读0次

    用过那么多软件,做为一个非专业人士,你是不是和我一样好奇,软件是怎么更新的?我的0.5版本怎么更新到1.0了?

    一、思考

    用我们已有的知识分解一个软件更新到底是怎么回事,这个神秘的面纱能不能被我们摘下来呢?
    先看看我们现在拥有什么?
    一个已经有的软件,当然是我自己做的了,小工具,为了实验,我们就排除其他干扰。


    那么怎么才能更新它呢?
    更新的实际情况是什么?其实是用另外一个软件代替这现有的这个,假设我们已经有另外一个版本了,叫什么好呢?就叫敦煌工具 v1.0.exe 好了。这个1.0肯定是放在一个地方的。更新就是从某个存储1.0的地方把他取过来放到原来版本存放的目录里。这样目录里就有两个文件,我们还要把原来的版本给删除掉。这样就只有一个新版本存在了。逻辑合不合理?内心窃笑。
    好了,貌似我们的思路十分合理,那么我们就可以开始了吗?
    慢着,我们还有问题,我们怎么知道现有版本不是最新的呢?
    我们需要有一个存储现有版本信息的文件,方便我们知道现有版本信息,并且应该在我们更新了版本也现时更新版本信息。
    那么那个存储新版本软件的地方呢?
    其实我们完全可以放到不同的文件夹来模拟,可是这样是不是太low了,显示不出功力不说,也没有那种真实的体验呀。怎么办?有了,我们可以放到服务器上,最好是有一个域名地址可以直接访问。这下应该齐活了,我们开工吧。

    二、预备工作

    经过前面的思考,我们已经知道有一些工作要提前做好了,我们先把这些工作做好。

    • 把新版本软件放到服务器
    • 制作一个储存版本信息的文件

    远程服务器

    我这里直接用了一个现有的服务器,我有一个博客网站,正好可以放这个文件,有一个网站多重要,哈哈。
    打开mobaxterm,使用sftp,把软件上传。


    原谅我涂的这么难看,不过高手太多,不这么涂的话,小站抗不住呀。
    我们看软件已经上传好了。域名地址测试下:https://geekala.com/敦煌工具.exe

    可以看到,在浏览器里输入这个网址 https://geekala.com/敦煌工具.exe, 直接就弹出了下载保存的对话框,说明我们的文件可以正常使用了,到时只要从这里下载更新我们本地的文件就好了。

    版本信息

    接下来我们还要先制作好一个版本信息文件,一般大家都偏爱xml,可是现在json这么流行,没理由不用呀,我们就直接使用json文件。
    创建一个update.json文件:
    内容如下:

    {
        "name": "敦煌工具",
        "version": 0.5,
        "version_url": "https://geekala.com/dhgate-tool.json",
        "download_url": "http://geekala.com/%E6%95%A6%E7%85%8C%E5%B7%A5%E5%85%B7.exe"
    }
    

    简单解释下:
    name 代表工具名称
    version 代表版本号
    version 代表远程服务里存储的版本信息,对的,我们肯定也需要在服务器里储存一个版本信息,不然怎么知道是否是新版本呢,当然也可以使用一个简单方式,就是直接在服务器里的软件名称上写上版本好就像是v1.0这样。
    download_url 代表去那儿下载,就是我们刚刚验证的那个网址,这里从网址上复制下来被转码了,直接用上面的地址 https://geekala.com/敦煌工具.exe 也能用。
    本地的已经做好了,服务器里的版本信息其实可以和本地一样,方便,当然有些内容不要也可以,但是一定要有一个关键信息就是version,这样就可以获取和本地version进行对比,知道有没有更新版本。

    三、开工了

    我们先写上思路,看看有没有还需要补充的。



    整个程序大概就是这样,现在可以开始写了。当然我们在写的过程中还可以再整理思路进行重构。

    # 获取本地版本信息
    with open('update.json', 'r', encoding='utf-8') as f:
        content = json.loads(f.read())
        print(content)
    # 输出
    (base) E:\测试软件更新>python update.py
    {
    'name': '敦煌工具', 
    'version': 0.5, 
    'version_url': 'https://geekala.com/dhgate-tool.json', 
    'download_url': 'http://geekala.com/%E6%95%A6%E7%85%8C%E5%B7%A5%E5%85%B7.exe'
    }
    

    获取本地版本信息,用json库读取文件内容,正常输出内容,这里要注意的是,open方法里要传入encoding='utf-8',因为我们的文件内容里有中文,如果不传入这个会出现乱码。

    # 获取服务器版本信息
    r_content = requests.get(content['version_url']).json()
    print(r_content)
    # 输出
    (base) E:\测试软件更新>python update.py
    {
    'name': '敦煌工具', 
    'version': 1.0, 
    'url': 'http://geekala.com/%E6%95%A6%E7%85%8C%E5%B7%A5%E5%85%B7.exe'
    }
    

    服务器版本信息,这里因为我没在服务版本文件里写下载地址,因些比本地少了一项,这里可以看到我把服务器版本信息里的version值设为了1.0,比本地的0.5要大,也就是有新版本。

    # 对比版本信息
    updated = False
    if content['version'] < r_content['version']:
        updated = True
    # 输出
    (base) E:\测试软件更新>python update.py
    True
    

    这里是比较版本信息,设置了一个默认值为False,如果有新版本就改为True.

    # 检查本地文件是否存在
    exist_file = path.exists(content['name'] + '.exe')
    print(exist_file)
    # 输出
    True
    

    为什么要做这个检测呢,主要是有可能会有人误删除文件,导致本地文件不存在了。当然我们后期是要把更新文件和启动做到一起的,那时候启动就会先做更新,当然是不存在文件不在的情况,那我为什么还要写?当然是因为我在写思路的时候写上了,不好意思说自己想错了呀,被人叫大神了,还会出错!(心虚,冷汗ing)

    # 如果有更新,下载服务器上的新版本
    if updated:
        appname = content['name'] + '_new.exe'
        try:
            urlretrieve(content['download_url'], appname)
        except (RuntimeError, ConnectionError):
            urlretrieve(content['download_url'], appname)
    

    如果有更新,就下载新版本,这里引入了from urllib.request import urlretrieve, 这个可以直接下载服务器上的文件,非常好用,强烈推荐,我以前下载妹子图的时候也经常用,(嗯 ,好像说了什么不该说的...)。
    try一下刚刚好,下载一般会遇到各种意外,遇到了我们再重新下载下。
    当然,如果这里直接使用requests下载二进制,再写入文件也是可以用的。不过麻烦了点。
    此时,我们的目录下应该会多出一个文件。


    下面就是要把原来的文件给删除掉。
    # 删除本地版本
    if exist_file:
        remove(content['name']+'.exe')
    

    这时前面的检查文件是否存在就可以用上了。如果老版本存在,就删除老版本。
    到了这里,我们先做一定的重构,方便后面使用:

    # 检查文件是否存在
    def check_exists(file):
        return path.exists(file)
    
    # 如果有更新,并且本地没有新版本
    # 下载服务器上的新版本
    appname = content['name'] + '_new.exe'
    if updated and not check_exists(appname):
        try:
            urlretrieve(content['download_url'], appname)
        except (RuntimeError, ConnectionError):
            urlretrieve(content['download_url'], appname)
    
    # 删除本地版本
    old_name = content['name'] + '.exe'
    if check_exists(old_name):
        remove(old_name)
    

    这样删除老版本就方便了,当然我这里是一步一步写下来,还有测试,所以做了这些修改,如果后面还有重构,可能就不需要再做检测。



    删除老版本后,目录里的新版本是这样的名字,我们还要修改名称变成原来的名字。

    # 更改新版本名称
    if check_exists(appname) and not check_exists(old_name):
        rename(appname, old_name)
    

    成功修改。到这时一切都很完美。

    四、更新本地版本信息

    经过上面的步骤,其实我们的目的已经基本上全部达成,可是还有一个最重要的步骤还没做,那就是更新本地版本信息文件,如果不做这一步,每次启动都会重新下载一次,更新一次,这显然不合理,我们应该只有在有新版本时才做更新。

    # 更新本地版本信息
    # 把远程版本号更新到本地
    content['version'] = r_content['version']
    with open(update_file, 'w', encoding='utf-8') as f:
        json.dump(content, f, ensure_ascii=False)
    

    查看一下更新后的update.json:

    {
        "name": "敦煌工具",
        "version": 1.0,
        "version_url": "https://geekala.com/dhgate-tool.json",
        "download_url": "http://geekala.com/%E6%95%A6%E7%85%8C%E5%B7%A5%E5%85%B7.exe"
    }
    

    可以看到,版本号已经更新,如果我们这时再运行脚本,程序将不会运行。

    五、第二次重构

    经过前面的初步尝试,我们能够达到目的,接下来让我们稍稍重构下。

    def main():
        # 版本文件
        update_file = 'update.json'
        # 本地版本信息
        content = get_json(update_file)
        # 获取服务器版本信息
        r_content = get_json(content['version_url'])
        # 老版本号与新版本号
        old = content['version']
        new = r_content['version']
        # 零时名称
        appname = content['name'] + '_new.exe'
        # 是否有新版本
        updated = is_updated(old, new)
        # 有新版本并且没有下载好的文件
        if updated and not check_exists(appname):
            print("开始下载...")
            download(content['download_url'], appname)
            print("下载完成!")
        # 删除本地版本
        old_name = content['name'] + '.exe'
        if updated and check_exists(old_name):
            print("删除老版本")
            remove(old_name)
        # 更改新版本名称
        if updated and check_exists(appname) and not check_exists(old_name):
            print("更新名称")
            rename(appname, old_name)
        # 更新本地版本信息
        # 把远程版本号更新到本地
        if updated:
            content['version'] = r_content['version']
            with open(update_file, 'w', encoding='utf-8') as f:
                print("更新版本信息")
                json.dump(content, f, ensure_ascii=False)
    # 输出
    (base) E:\测试软件更新>python update.py
    开始下载...
    下载完成!
    删除老版本
    更新名称
    更新版本信息
    

    好了,现在我们已经有了一个重构过的版本,功能也能满足我们需求。软件更新的初步尝试,已经完美的达到。下一篇我们将尝试把这个代码与启动结合在一起,让软件在启动时能够自查并且更新版本。

    后记

    服务器上的软件我会删除,大家要做尝试的就放过我的小站了。不过如果有任何问题,欢迎交流,也可以关注我的公众号追更哦。全部代码可以在公众号输入关键词 软件更新 获取。

    相关文章

      网友评论

          本文标题:怎么实现软件更新?

          本文链接:https://www.haomeiwen.com/subject/yzvziqtx.html