利用paramiko、tkinter、os等模組實現遠端連線sftp,並進行資料夾的遞迴式定時傳輸
阿新 • • 發佈:2019-02-16
from tkinter import *
製作GUI介面
import paramiko
用來遠端
import time
from tqdm import tqdm
用來顯示程序
import os
import threading
本打算呼叫多執行緒來完成更新檔案,來達到更快的速度。但這樣的話,需要將run函式查分成多個函式,如果數量多的話您可以自行考慮
class Application(Frame):
def init(self, master):
super(Application, self).init(master)
# 繼承超類/父類的初始化
Label(self, text='Remote Data Dir').grid(row=0, column=0, sticky=W, padx=0, pady=0) default_remote_dir = StringVar() # 字元變數 default_remote_dir.set(r'/備站備份/') self.remote_dir_ent = Entry(self, textvariable=default_remote_dir, width=52) # width值是自己摸索的 grid()來佈局需要自己重複的實驗,以求找到最合適的大小 self.remote_dir_ent.grid(row=0, column=1, columnspan=3, sticky=W, padx=0, pady=0) Label(self, text='Local Data Dir').grid(row=1, column=0, sticky=W, padx=0, pady=0) default_local_dir = StringVar() default_local_dir.set(r'D:\ljh\untitled\sdv') self.local_dir_ent = Entry(self, textvariable=default_local_dir, width=52) self.local_dir_ent.grid(row=1, column=1, columnspan=3, sticky=W, padx=0, pady=0) Label(self, text='Remote IP').grid(row=2, column=0, sticky=W, padx=0, pady=0) default_ip = StringVar() default_ip.set('10.127.84.11') self.ip_ent = Entry(self, textvariable=default_ip) self.ip_ent.grid(row=2, column=1, sticky=W, padx=0, pady=0) Label(self, text='Remote Port').grid(row=2, column=2, sticky=W, padx=0, pady=0) default_port = IntVar() default_port.set(22) self.port_ent = Entry(self, textvariable=default_port) self.port_ent.grid(row=2, column=3, padx=0, pady=0) Label(self, text='Username').grid(row=3, column=0, sticky=W, padx=0, pady=0) default_username = StringVar() default_username.set('Backup') self.username_ent = Entry(self, textvariable=default_username) self.username_ent.grid(row=3, column=1, sticky=W, padx=0, pady=0) Label(self, text='Password').grid(row=3, column=2, sticky=W, padx=0, pady=0) default_password = StringVar() default_password.set('re955426') self.password_ent = Entry(self, textvariable=default_password, show='*') self.password_ent.grid(row=3, column=3, padx=0, pady=0) Label(self, text='time interval(h)').grid(row=4, column=0, sticky=W, padx=0, pady=0) default_time_interval = IntVar() default_time_interval.set(24) self.time_interval_ent = Entry(self, textvariable=default_time_interval) self.time_interval_ent.grid(row=4, column=1, sticky=W, padx=0, pady=0) self.bttn = Button(self, text='Start ', command=self.run) self.bttn.grid(row=5, rowspan=2, column=0, columnspan=2, sticky=W, padx=0, pady=0) # 原本打算做出一個顯示更新資訊的文字,但不會非同步更新。因此先放到一邊 # self.txt = Text(self, wrap=WORD) # self.txt.grid(row=5, rowspan=2, column=0, columnspan=4, padx=0, pady=0) # self.scroll = Scrollbar(self, command=self.txt.yview, width=15) # self.txt.configure(yscrollcommand=self.scroll.set) # self.scroll.grid(row=5, rowspan=2, column=5, sticky=N+S) self.grid() def run(self): ip = self.ip_ent.get() port = self.port_ent.get() port = int(port) username = self.username_ent.get() password = self.password_ent.get() # 以上得到GUI中Entry中的輸入值 transport = paramiko.Transport((ip, port)) transport.connect(username=username, password=password) sftp = paramiko.SFTPClient.from_transport(transport) # 連線遠端sftp,並例項化一個遠端會話 remote_source_dir = self.remote_dir_ent.get() local_source_dir = self.local_dir_ent.get() print('Running Update time: ' + str(time.ctime())) while True: # 用來定時更新,所以用的死迴圈 local_walks = os.walk(local_source_dir) # 考慮到資料夾的遞迴問題即資料夾裡還有資料夾,因此呼叫os.walk()a方法, 其返回一個迭代器,3維陣列,第一個為 # 當前的目錄,第二個為當前路徑下的目錄,第三個為當前目錄下的檔名 year = time.localtime()[0] month = time.localtime()[1] day = time.localtime()[2] today_files = '{:4}{:02}{:02}'.format(year, month, day) # 此處構造了一個有關當天時間的字串,因為我這邊是不要更新今天生成的不完整的檔案的 # (檔案在第二天會在此有其他軟體生成),這裡的思想是構造你需要過濾的檔案,在一次更新中不更新此類檔案 local_walks = list(local_walks) # 將迭代器變為列表,因為需要迴圈呼叫 for i in local_walks: local_dirname = i[0] dirs = i[1] files = i[2] # 此處的local_dirname、dirs、files就是上述os.walk()返回的3維陣列的內容 dirname = i[0].replace(local_source_dir, '') dirname = dirname.replace('\\', '') # 此處得到遞迴的資料夾的名字,方便與遠端的伺服器中的地址構成新的目錄,從而傳輸檔案 if dirname != '': remote_dirname = remote_source_dir + dirname + '/' else: remote_dirname = remote_source_dir + dirname # 得到新的遠端目錄 for t in range(1): try: sftp.mkdir(remote_dirname) print('Create remote dir: ' + remote_dirname + '!') except: continue # 檢查是否需要新建資料夾,因為sftp中我沒有看到類似於os.path.isdir()的方法,因此考慮用try來解決 # for迴圈是為了配合continue remote_source_dirs = sftp.listdir(remote_dirname) # 得到遠端伺服器下當前目錄下的檔案和目錄列表 transport_files = set(files) - set(remote_source_dirs) transport_files = list(transport_files) # 利用集合的相減得到要更新的檔案。注意此處是單純的檔案。 for j in transport_files: if today_files in j: transport_files.remove(j) elif j in dirs: transport_files.remove(j) # 此處為過濾不需要更新的檔案,elif部分可以考慮不要 for j in tqdm(transport_files, bar_format='{l_bar}{bar}'): # 方法tqdm可以顯示更新的進度 file_path = local_dirname + '\\' + str(j) remote_file_path = remote_dirname + str(j) # 構造本地和遠端的檔案的路徑 sftp.put(file_path, remote_file_path) # 將檔案從本地上傳到伺服器 print('Remote directory: ' + remote_dirname + '\'s updating is completed!') interval = self.time_interval_ent.get() interval = int(interval) # get()得到的是字串,因此強制轉換成整型 time.sleep(interval // 4) # 設定休眠時間 print('Running Update time: ' + str(time.ctime()))
if name == ‘main‘:
root = Tk()
root.title(‘SFTP_資料備份’)
root.geometry(‘475x160’)
app = Application(root)
root.mainloop()