1.绿色软件的升级,要用到三个目录:
1)主版本文件的目录mainVersionDir
主要存放完整的压缩包
2)子版本文件所在的目录subVersionDir
主要存放要升级的单个文件或者多个文件,多个文件一般以zip的形式存放
3)最新文件所在的目录latestVersionDir
主要存放最新版本的软件包,该包是由主版本压缩包和子版本中的各个文件组装而成
2.基本思路:
1)将最新版本的zip文件解压到同级的tmp目录中
2)如果是单文件升级,则将子版本的文件拷贝到1步骤中的tmp目录里;如果是多文件升级,则首先需要将多文件解压,然后再拷贝
3)将步骤2)替换后的tmp文件重新压缩,并放到新版本的位置
3.用到的python模块以及改进:
主要用到了zipfile和shutil模块。
默认情况下,zip压缩会保留根目录,我们这里不保留根目录
shutil中copytree用来对文件夹进行复制,但是比较遗憾的是,如果目标文件已经存在的话,该函数就会报错抛异常了。引用http://blog.chinaunix.net/uid-200142-id-3492546.html的方法,修改源码,使其默认可以支持文件和文件夹的覆盖。
4.代码逻辑:
# coding:utf-8
import sys, os, time
import zipfile
import shutil
def print_usage():
print '''usage:
python software_upgrade.py subZipFullPath, targetZipFullPath, mainVersionZipPath[, relativePath]\n
for example:
linux platform:
python software_upgrade.py /test/software_upgrade/subVersionDir/readme.txt /test/software_upgrade/latestVersionDir/myAPP_latest.zip /test/software_upgrade/mainVersionDir/myAPP_main.zip /readme.txt
python software_upgrade.py /test/software_upgrade/subVersionDir/myAPP_subVersion_0.1.zip /test/software_upgrade/latestVersionDir/myAPP_latest.zip /test/software_upgrade/mainVersionDir/myAPP_main.zip
windows platform:
python software_upgrade.py C:\software_upgrade\subVersionDir\readme.txt C:\software_upgrade\latestVersionDir\myAPP_latest.zip C:\software_upgrade\mainVersionDir\myAPP_main.zip \readme.txt
python software_upgrade.py C:\software_upgrade\subVersionDir\myAPP_subVersion_0.1.zip C:\software_upgrade\latestVersionDir\myAPP_latest.zip C:\software_upgrade\mainVersionDir\myAPP_main.zip
'''
def my_copytree(src, dst, symlinks=False):
"""
若同样的目标已存在则不动
Args:
src:
dst:
symlinks:
Returns:
"""
names = os.listdir(src)
if not os.path.isdir(dst):
os.makedirs(dst)
errors = []
for name in names:
srcname = os.path.join(src, name)
dstname = os.path.join(dst, name)
try:
if symlinks and os.path.islink(srcname):
linkto = os.readlink(srcname)
os.symlink(linkto, dstname)
elif os.path.isdir(srcname):
my_copytree(srcname, dstname, symlinks)
else:
if os.path.isdir(dstname):
os.rmdir(dstname)
elif os.path.isfile(dstname):
os.remove(dstname)
shutil.copy2(srcname, dstname)
# XXX What about devices, sockets etc.?
except (IOError, os.error) as why:
errors.append((srcname, dstname, str(why)))
# catch the Error from the recursive copytree so that we can
# continue with other files
except OSError as err:
errors.extend(err.args[0])
try:
shutil.copystat(src, dst)
except WindowsError:
# can't copy file access times on Windows
pass
except OSError as why:
errors.extend((src, dst, str(why)))
if errors:
pass
# raise Error(errors)
def unzip(filename, filedir):
"""
Args:
filename: 'foobar.zip' #要解压的文件
filedir: 解压后放入的目录
Returns:
"""
r = zipfile.is_zipfile(filename)
if r:
starttime = time.time()
fz = zipfile.ZipFile(filename, 'r')
for file in fz.namelist():
# print(file) # 打印zip归档中目录
fz.extract(file, filedir)
endtime = time.time()
times = endtime - starttime
else:
print('This file is not zip file')
print('[unzip file] time costs:' + str(times))
def zip(path, filename):
"""
默认情况下,zip压缩会保留根目录,我们这里不保留根目录
Args:
path: 要进行压缩的文档目录
filename: 'foobar.zip' # 压缩后的文件名
Returns:
"""
try:
import zlib
compression = zipfile.ZIP_DEFLATED # 压缩方法
except:
compression = zipfile.ZIP_STORED
starttime = time.time()
# start = path.rfind(os.sep) + 1 # os.sep 分隔符 (保留根目录)
start = len(path) # (不保留根目录)
z = zipfile.ZipFile(filename, mode="w", compression=compression)
try:
for dirpath, dirs, files in os.walk(path):
for file in files:
if file == filename or file == "zip.py":
continue
# print(file)
z_path = os.path.join(dirpath, file)
z.write(z_path, z_path[start:])
z.close()
endtime = time.time()
times = endtime - starttime
except:
if z:
z.close()
print('[create zip file] time costs:' + str(times))
def create_latest_package(subVersionFullPath, latestVersionFullPath, mainVersionFullPath, relativePath):
"""
Args:
subVersionFullPath:
latestVersionFullPath: latest_Zip_path
mainVersionFullPath:
relativePath:
Returns:
"""
if os.path.exists(subVersionFullPath) and os.path.exists(mainVersionFullPath): # 判断子版本和主版本zip包是否存在
if not os.path.exists(latestVersionFullPath):
# 如果不存在最新版本的zip包,将子版本的最新文件与主版本的文件进行组装,然后将组装后的文件压缩到最新版本所在的目录
oper(subVersionFullPath, mainVersionFullPath, latestVersionFullPath, relativePath)
else:
# 如果存在最新版本的zip包,将子版本的最新文件与最新版本的文件进行组装,然后将组装后的文件压缩到最新版本所在的目录
oper(subVersionFullPath, latestVersionFullPath, latestVersionFullPath, relativePath)
else:
print "not exists {0} or {1}".format(subVersionFullPath, mainVersionFullPath)
def oper(srvZipPath, destZipPath, outZipPath, relativePath):
"""
注意:多文件替换时,一般多文件存在压缩包中,所以需要解压
基本思路 1.将最新版本的zip文件解压到同级的tmp目录中
2.如果是单文件升级,则将子版本的文件拷贝到1步骤中的tmp目录里;如果是多文件升级,则首先需要将多文件解压,然后再拷贝
3.将步骤2替换后的tmp文件重新压缩,并放到新版本的位置
Args:
srvZipPath: 子版本zip路径
destZipPath: 目标版本zip路径
outZipPath: 组装后,生成新的zip文件的完整路径
relativePath: 单文件和多文件标识,若为None则表明多文件替换,否则单文件替换
Returns:
"""
# 若最终生成的zip文件所在的路径不存在,则创建
outZipPathDir = os.path.dirname(outZipPath)
os.makedirs(outZipPathDir) if not os.path.exists(outZipPathDir) else ''
# 确定临时文件的路径
srvZipPathTmp = os.path.dirname(srvZipPath) + os.sep + "tmp"
destZipPathTmp = os.path.dirname(destZipPath) + os.sep + "tmp"
print "[unzip target zip] srv:{0} dest:{1}".format(destZipPath, destZipPathTmp)
unzip(destZipPath, destZipPathTmp) # 解压目标zip文件
if relativePath: # 单文件和多文件标识,若为None则表明多文件替换,否则单文件替换
print "[single file replace] srv:{0} dest:{1}".format(srvZipPath, destZipPathTmp + relativePath)
shutil.copy(srvZipPath, destZipPathTmp + relativePath) # 将要升级的文件拷贝到已经解压开的目标zip文件destZipPathTmp中
else:
print "[unzip source zip] srv:{0} dest:{1}".format(srvZipPath, srvZipPathTmp)
unzip(srvZipPath, srvZipPathTmp)
print "[replace files] srvZipPathTmp:{0} destZipPathTmp:{1}".format(srvZipPathTmp, destZipPathTmp)
my_copytree(srvZipPathTmp, destZipPathTmp) # 将要升级的文件拷贝到已经解压开的目标zip文件destZipPathTmp中
print "[create new zip file] srv:{0} dest:{1}".format(destZipPathTmp, outZipPath)
zip(destZipPathTmp, outZipPath)
# 删除临时文件
shutil.rmtree(srvZipPathTmp) if os.path.exists(srvZipPathTmp) else ''
shutil.rmtree(destZipPathTmp) if os.path.exists(destZipPathTmp) else ''
print "success"
if __name__ == '__main__':
number = 0
for i in sys.argv:
print "arg{0}: {1}".format(number, i)
number += 1
if len(sys.argv) < 4 or len(sys.argv) > 5:
print_usage()
if len(sys.argv) == 4:
create_latest_package(sys.argv[1], sys.argv[2], sys.argv[3], None)
elif len(sys.argv) == 5:
create_latest_package(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4])
5.效果展示:
1)单文件情况:在命令行中执行:python software_upgrade.py C:\software_upgrade\subVersionDir\readme.txt C:\software_upgrade\latestVersionDir\myAPP_latest.zip C:\software_upgrade\mainVersionDir\myAPP_main.zip \readme.txt
效果如下
2)单文件情况:在命令行中执行:python software_upgrade.py C:\software_upgrade\subVersionDir\myAPP_subVersion_0.1.zip C:\software_upgrade\latestVersionDir\myAPP_latest.zip C:\software_upgrade\mainVersionDir\myAPP_main.zip
效果如下: