python 打包的 exe 程序自动更新

看了一下网友的思路,大致都如下:

  1. 运行主程序,同时检查是否有更新

  2. 下载更新包

  3. 退出主程序

  4. 解压更新包,覆盖旧版本的文件(主程序退出以后,才能解压覆盖)

这里的问题就是如何覆盖旧版本文件,因为 exe 不像 py 文件,py 文件运行时是可以自己删除自己,或者覆盖自身的,譬如:

test.py

import subprocess

subprocess.Popen('del test.py', shell=True)  # 运行后,会删除自身:test.py

但是一旦你将它打包成 .exe 文件,就不行了。这是因为 exe 本身在执行时,文件是被占用的状态,而我们不能在文件占用的时候删除自身。

所以想要解决这个问题,就需要先制作一个程序,这个程序和主程序完全独立,作为一个独立的进程来运行。它专门用来进行更新包的解压覆盖,假设叫 update.exe, 下面是一个伪代码,来模拟更新:

主程序:

if old_version < new_version:
    download(file)                 # 下载更新包
    subprocess.Popen('update.exe -args')  # update.exe 这个程序启动时,可以先延迟两秒,等待当前程序退出,然后自动去解压更新包,来覆盖主程序, 想要传递参数,可以将 update.exe 写成一个可接受命令行参数的程序,来传递主程序的安装位置等信息
    sys.exit()                     # 退出主程序

更新程序:update.exe

import time


def unzip(src, dst):
    ...


if __name__ == "__main__":
    time.sleep(2)
    unzip(src, dst)

下面的代码来源于 CSDN博主「Linuxnot」https://blog.csdn.net/u013193899/article/details/78686039

这个例子更加简洁直观,它临时写一个 bat 文件,来删除和运行新程序

import os
import sys
import subprocess 

#编写bat脚本,删除旧程序,运行新程序
def WriteRestartCmd(exe_name):
    b = open("upgrade.bat",'w')
    TempList = "@echo off\n";                           # 关闭bat脚本的输出
    TempList += "if not exist "+exe_name+" exit \n";    # 新文件不存在,退出脚本执行
    TempList += "sleep 3\n"                             # 3秒后删除旧程序(3秒后程序已运行结束,不延时的话,会提示被占用,无法删除)
    TempList += "del "+ os.path.realpath(sys.argv[0]) + "\n"    # 删除当前文件
    TempList += "start " + exe_name                     # 启动新程序
    b.write(TempList)
    b.close()
    subprocess.Popen("upgrade.bat")
    sys.exit()                                         # 进行升级,退出此程序

def main():
    # 新程序启动时,删除旧程序制造的脚本
    if os.path.isfile("upgrade.bat"):
        os.remove("upgrade.bat")
    WriteRestartCmd("newVersion.exe")

if __name__ == '__main__':
    main()
    sys.exit()

打包的坑

当用 pyinstaller 打包成一个单独的 exe 执行程序时,不要在脚本中使用 __file__ 来作为当前程序的路径(打包成文件夹形式没关系,但如果使用 pyinstaller -F 打包成单独的exe就不行),在 .py 文件中这个路径是正确的,但是一旦你将 .py 文件打包成 .exe, 这个路径就不对了! 譬如下面的脚本:

print(__file__)

当作 py 文件运行时,结果为: C:\Users\UNCO9CA\Desktop\test\test.py,这是正确的路径。

打包成单独的 exe 文件时(即使用 pyinstaller -F test.py 选项打包):

C:\Users\UNCO9CA\Desktop\test\dist>test.exe
C:\Users\UNCO9CA\AppData\Local\Temp\1\_MEI114762\test.py  # 跟预想的不一样!这是因为当打包成一个单独的 exe 文件后,运行它时它会现在一个临时文件夹解压然后运行,所以路径就变得不可知了。而且后缀依然是 .py 

因此,不要使用 __file__ 来获取运行程序的路径,而是使用: sys.argv[0]

posted @ 2022-04-07 22:05  wztshine  阅读(4228)  评论(2编辑  收藏  举报