1. 程式人生 > >利用paramiko、tkinter、os等模組實現遠端連線sftp,並進行資料夾的遞迴式定時傳輸

利用paramiko、tkinter、os等模組實現遠端連線sftp,並進行資料夾的遞迴式定時傳輸

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()