从sftp服务器下载文件到本地,这是一个简单的需求,使用paramkio模块就可以实现,那么如何在项目运行过程中定期从sftp端拉下来要更新的项目文件,并且更新到项目中。
从sftp服务器下载文件到本地
将sftp服务器的文件下载到本地临时创建的文件夹:
import paramiko
class UpdateFile:
def __init__(self,host,username,password,port=22):
self.sf = paramiko.Transport((host,port)) #连接服务器
self.sf.connect(username=username,password=password) # 登录
self.sftp = paramiko.SFTPClient.from_transport(self.sf) #创建操作对象
def isDir(self,sftp,path): # 判断远程的文件对象是目录还是文件
try:
sftp.chdir(path) #尝试进入下一级
sftp.chdir('..')
return True
except:
return False
def downloadDir(self,downDir,remoteDir): # 下载文件到本地
if not os.path.isdir(downDir): #先判断本地临时文件夹是否存在
os.makedirs(downDir)
self.sftp.chdir(remoteDir) #进入远程目录
remote_files = self.sftp.listdir() #查看当前远程目录下的所有文件
for r_f in remote_files:
downpath = os.path.join(downDir,r_f) #拼接好本地要保存的路径
if r_f in ['__pycache__', '.idea','.svn',os.path.basename(__file__)]: #不去下载的文件
pass
else:
if not os.path.exists(downpath): #先判断本地目录是否存在
if self.isDir(self.sftp, r_f): #再判断当前对象在远程是否为目录
os.makedirs(downpath) #如果远程是目录,则在本地创建对应的目录
self.downloadDir(downpath, r_f) #同时下载该目录下的所有文件
else:
open(downpath,'wb') #如果不是目录,则创建文件
self.sftp.get(r_f, downpath) #同时将文件下载到本地路径中
else: #本地对应目录已存在,则直接下载
if self.isDir(self.sftp, r_f):
self.downloadDir(downpath, r_f)
else:
self.sftp.get(r_f, downpath)
self.sftp.chdir('..')
return True
将下载的文件更新到本地对应的文件中
采用深度遍历的方法,从临时文件夹中将从sftp下载的文件依次更新到项目中:
def updateFiles(localDir,downDir): # 更新文件
stack = [] #创建栈
stack.append(downDir) #将下载的文件路径压入栈中,准备遍历
while True:
if len(stack) == 0:
shutil.rmtree(os.path.abspath('.') + r'\backup') # 表示更新完成,栈中已经没有内容,则删除备份文件夹
return True
dir_ = stack.pop()
ds = os.listdir(dir_)
for dname in ds:
dpath = os.path.join(dir_,dname) # 下载下来的文件路径
# backup在这里是我们设置的临时文件夹,我们要从其后匹配出本地项目中有的路径,与本地路径拼接
relative_path = re.compile(r'.+\\backup(.+)').findall(dpath)[0]
wpath = localDir+relative_path # 要更新的文件路径
if os.path.isdir(dpath):
if not os.path.exists(wpath):
os.makedirs(wpath)
stack.append(dpath) # 是目录则加入栈中
else:
try:
#打开本地文件同时读取下载的文件写入到本地文件,这一步不能分开执行,否则会造成文件丢失
open(wpath,'wb').write(open(dpath, 'rb').read())
except Exception as e:
print(e)
return False
在项目运行中更新文件
这里将调用更新放在了后台定时任务中,设定情景:定期请求中心服务器获取当前项目的最新版本号和sftp服务器地址信息,本地config文件中配有当前项目的版本号,进行对比,如果版本不同,则去从sftp服务器下载文件更新到项目中,同时更新本地版本号。这里只写了任务,任务添加到任务对象中就不写了:
impot config
Now_ver = config.VER
def updateVer(self):
r = requests.get(url_updatefile, timeout=TIMEOUT)
res_dict = json.loads(r.text)
print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), '获取项目文件最新版本...')
if res_dict['status'] == 0:
new_ver = res_dict['data']['version']
ftp = res_dict['data']['ftp']
# 将本地版本号与远程版本号对比
if int(new_ver) == int(Now_ver): # 已经是最新版本
pass
else:
try:
#从中心服务器请求到的sftp是链接形式的:sftp://user:passwd@host:port/远程地址
regex = re.compile(r'sftp://(\w+):(\w+)@(.*?):(\d+)/(.*)') #使用正则提取关键信息
m_s = regex.findall(ftp)
print(m_s)
username, password, host, port, remoteDir = m_s[0]
f = UpdateFile(host, username, password, int(port)) #链接sftp
downtag = f.downloadDir(os.path.abspath('.')+r'\backup', '/'+remoteDir) #下载文件到本地临时文件夹
if downtag: #如果下载完成,则将临时文件夹中的内容更新到项目中
updatetag = updateFiles(os.path.abspath('.'), os.path.abspath('.')+r'\backup')
if updatetag: #如果项目也更新成功,则将最新版本号写入到config文件中
# 更新版本号
with open('config.py', 'r+',encoding='utf-8') as s:
lines = s.readlines()
s.seek(0, 0)
for line in lines:
line_new = line.replace('VER = '+str(Now_ver), 'VER = '+new_ver)
s.write(line_new)
# time.sleep(2)
# os.system('../reload.bat')
else:
pass
else:
pass
except Exception as e:
print(e)
ext.writelog('更新文件出错!')
以上就是完整的在项目运行过程中定期从sftp下载有更新的文件,并将更新的文件同步到本地项目中。