前言
在现在的软件开发和使用中,软件的更新是很关键的一环。通过不停的更新软件,迭代,给用户带来更好的体验和更多的功能以及修复用户反馈的bug。我们在更新的软件的时候,如果每次都要用户从新安装软件的话,想必体验也不会很好,也很麻烦,浪费不必要的资源。
那么今天就继续给大家说要一下软件更新服务里面的客户度端更新 。有些后面关注的同学没看到前面写的那篇服务端搭建的文章的话,可以待会出门左拐看一下。
搭建起界面
在这次客户端编写过程当中,为了给大家可以看到更加直观的效果就简单做了个界面,使用到了以下技术:
- PyQt5
- Python3
- python序列化
- urllib下载文件
大家先在pycharm中,把pyqt5给装上,pip install PyQt5,也可以在设置的里面的project interpreter 里面下载安装。
接着,就配置好外部工具,QtDesigner以及PyUIC这两个外部工具。
由于这两个工具的配置比较简单,我就不作说明了,大家可以自行百度一下,随意参考一种都可以顺利配置好这两个外部工具的。
打开designer绘制
接着,打开designer来对界面进行绘制设计。
大概就这样就可以了,一个标题、一个文本框、两个按钮就足够显示了。
虽然长得丑了一点,但是我们这次文章的关键不是界面的美化,所以就不作其他处理了。
pyuic 生成代码
保存好,再到pycharm里面对ui文件运行一下PYUIC就OK
这个时候,界面代码已经由设计图转成Python代码了,可以看到它生成了一个类,但是还跑不动,因为没有引用到它。所以,我们先放它到一边。
创建新文件和类继承生成的界面
接着,我们就重新创建好一个py文件,导入我们预期就要用到的库。
- hashlib 计算MD5用
- sys
- pickle 计算序列化
- urllib 下载文件
- urllib3
- os 处理文件 和目录
这几个库导进来后,就开始新建一个类,继承刚才生成的那个界面类。写法如下
继承参考代码
1 2 3 4 5 6 7 8 9 10 11
| class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setupUi(self)
if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) mainWindow = MainWindow() mainWindow.show() sys.exit(app.exec_())
|
这个时候,运行文件就可以显示出界面了。而且这样将界面和实际操作的代码分隔开来可以避免重新UIC界面时,代码会丢失,而导致要重写的问题。
着手准备实现 更新功能
OK,界面出来后,就要实现功能了。更新,更新怎么做?
上一篇更新服务文章已经提供了更新说明和下载服务了,所以,我们这次就利用好这些接口。
我们又新建一个类,专门用来处理更新文件的,所以就简单的叫UpdateFiles吧。
在这里再过一次流程:
- 从服务器上面拿到清单文件
- 反序列清单文件
- 比较里面的数据,如果文件存在则计算MD5,相同就跳过,不相同就准备下载
- 如果文件不存在,则不用计算MD5了,直接准备下载
所以,就需要3个函数,下载文件函数、计算MD5值函数、检查更新函数
按照这三个需求以及之前文章服务端的接口,我们可以很快就完成代码的编写。
在UpdateFiles这个类中完成这几个功能的开发后,便可以很方便的完成更新文件的服务功能。
由于已经整理成一个类,所以,后面如果各位希望可以在自己代码中用到这样子的更新服务的话吗,就可以直接将这个类copy过去,改一些东西就可以用了。
说说踩的坑
这里说一下写代码踩的坑吧
1、因为用的是urllib去下载的文件,多级目录下,文件不存在的时候,并不会在目标目录自动新建目录。所以,在下载的时候,需要对路径进行处理,筛选出目标目录,mkdir创建一下目录后,再下载目标文件。具体实现可以看downloadFiles函数,代码很简单,理解起来肯定没问题。
实现效果
点击检查更新会将需要更新的文件列在上面
点击下载便会自动下载文件了。而且有个简单的进度条可以看到进度。
代码呈上
代码我就直接贴出来了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
|
import json import hashlib import sys import pickle from PyQt5 import QtWidgets from urllib3 import request from urllib import request import os
from updateServer.UClient.sample import Ui_MainWindow
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setupUi(self) self.pushButton.clicked.connect(self.showUpdate) self.pushButton_2.clicked.connect(self.updateNow) self.updateList = [] self.updateServer = UpdateFiles()
def showUpdate(self): self.textBrowser.clear() self.updateList = self.updateServer.check_update() for j in self.updateList: print(j) self.textBrowser.append(j)
def updateNow(self): all_file_number = 0 for j in self.updateList: self.updateServer.downloadFiles(j) all_file_number += 1 vau = int((all_file_number * 100) / len(self.updateList)) self.progressBar.setValue(vau) self.repaint()
class UpdateFiles(): def __init__(self): self.server = '47.101.195.58' self.port = '1213' self.directory = os.getcwd()
def downloadFiles(self, key): checkurl = 'http://' + self.server + ':' + self.port file_dir = self.directory + '\\' + key file_dir = file_dir.replace('/', '\\') if os.path.exists(file_dir): os.remove(file_dir) request.urlretrieve(checkurl + '/' + key, file_dir) else: newpath = '\\'.join(file_dir.split('\\')[:-1:]) print(newpath) try: os.mkdir(newpath) request.urlretrieve(checkurl + '/' + key, file_dir) except: request.urlretrieve(checkurl + '/' + key, file_dir)
def Getfile_md5(self, filename): if not os.path.isfile(filename): return myHash = hashlib.md5() f = open(filename, 'rb') while True: b = f.read(8096) if not b: break myHash.update(b) f.close() return myHash.hexdigest()
def check_update(self): data = {} updateList = [] checkurl = 'http://' + self.server + ':' + self.port request.urlretrieve(checkurl + '/.listFile', ".listFile")
with open(".listFile", "rb") as f: data = pickle.load(f) print(data) for key in data: new_md5 = data[key] file_dir = self.directory + '\\' + key if os.path.exists(file_dir): oldmd5 = self.Getfile_md5(file_dir) if oldmd5 != new_md5: print(new_md5, "准备下载") updateList.append(key) else: updateList.append(key) print('准备下载', file_dir) return updateList
if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) mainWindow = MainWindow() mainWindow.show() sys.exit(app.exec_())
|
结语
好了,就到这里吧,这篇是综合上一篇服务端搭建写的。
打算将这个软件更新服务写几篇出来的,这里是第二篇,大概说了一下客户端该怎么处理。
后面打算给大家讲一下热更新,如何实现无须重启便完成软件的更新这样子。
之前那篇服务端搭建的代码存在一些bug,我已经更新了。然后连同本次代码,将整个服务端以及客户端一起提交到GitHub中了。欢迎大家到GitHub中fork使用,如果能给个star就 最好啦。
点击这里访问GitHub项目 SoftwareUpdateServer
要是感觉不错的话,就留个言评论一下下呀,欢迎各位大哥指正