使用paramiko的SFTP get或put整個目錄


  在《使用paramiko執行遠程linux主機命令》中舉例說明了執行遠程linux主機命令的方法,其實paramiko還支持SFTP傳輸文件。

  由於get或put方法每次只能傳輸一個文件,而不是整個目錄,因此我們先看一下傳輸單個文件的方法,其實非常簡單,網上也有很多參考資料了。

  還是直接使用前文中定義的類,我們添加兩個方法即可(本文中不需要使用的方法先用pass代替了):

 1 # 定義一個類,表示一台遠端linux主機
 2 class Linux(object):
 3     # 通過IP, 用戶名,密碼,超時時間初始化一個遠程Linux主機
 4     def __init__(self, ip, username, password, timeout=30):
 5         self.ip = ip
 6         self.username = username
 7         self.password = password
 8         self.timeout = timeout
 9         # transport和chanel
10         self.t = ''
11         self.chan = ''
12         # 鏈接失敗的重試次數
13         self.try_times = 3
14 
15     # 調用該方法連接遠程主機
16     def connect(self):
17          pass
18 
19     # 斷開連接
20     def close(self):
21         pass
22 
23     # 發送要執行的命令
24     def send(self, cmd):
25         pass
26 
27     # get單個文件
28     def sftp_get(self, remotefile, localfile):
29         t = paramiko.Transport(sock=(self.ip, 22))
30         t.connect(username=self.username, password=self.password)
31         sftp = paramiko.SFTPClient.from_transport(t)
32         sftp.get(remotefile, localfile)
33         t.close()
34 
35     # put單個文件
36     def sftp_put(self, localfile, remotefile):
37         t = paramiko.Transport(sock=(self.ip, 22))
38         t.connect(username=self.username, password=self.password)
39         sftp = paramiko.SFTPClient.from_transport(t)
40         sftp.put(localfile, remotefile)
41         t.close()

  注意上面的remotefile, localfile一定是文件,不能是目錄,對於sftp_get,localfile指定本地將要保存的文件名,可以與remotefile的名字不一樣;

  而sftp_put中,remotefile指定遠端將要保存的文件名,可與localfile的名字不一樣,測試代碼如下:

 

 1 if __name__ == '__main__':
 2     remotefile = r'/home/sea/test/xxoo.txt'
 3     localfile = r'E:\PythonFiles\Learn\ooxx.txt'
 4 
 5     host = Linux('192.168.180.128', 'root', '1234')
 6 
 7     # 將遠端的xxoo.txt get到本地,並保存為ooxx.txt
 8     host.sftp_get(remotefile, localfile)
 9 
10     # # 將本地的xxoo.txt put到遠端,並保持為xxoo.txt
11     # host.sftp_put(localfile, remotefile)

 

  

  下面再來考慮下如何傳輸整個目錄?

  有兩種思路:

  1 如果是要get則采用已經定義的connect方法連接到linux主機,然后通過send方法執行tar命令將需要傳輸的整個目錄打包,再傳輸打包后的文件即可,如果是put則需在本地打包

        該方法的缺點是:在遠端或者本地進行打包或者解壓,並且打包會占用臨時存儲空間,如果是遠端打包還需先SSH鏈接linux主機。

        優點是:不用做目錄掃描處理。

      2 遍歷需要get或put的目錄及其子目錄,然后依次傳輸每一個文件。優點是不需要SSH登陸和打包解壓,缺點是需要做目錄掃描,但是目錄掃描是很簡單的,因此我們采用這種方法。

 

  先來看看Get,由於要掃描目錄,因此先定義一個方法用來對指定目錄進行掃描,找出該目錄及所有子目錄中的所有文件。

  那么問題來了,怎么掃描目錄呢?使用python的os庫的方法嗎?肯定是不行的,因為python的os庫的方法都是對本地目錄或文件的操作,它是無法操作遠程linux主機上的文件和目錄的。

  其實paramiko的SFTP接口提供了操作遠端linux主機上的文件和目錄的方法,只要建立了與遠端的SFTP連接后,就可以執行文件和目錄操作。

  下面是獲取遠端linux主機上指定目錄及其子目錄下的所有文件的方法,也是定義在上面的類中的。

 1 # ------獲取遠端linux主機上指定目錄及其子目錄下的所有文件------
 2     def __get_all_files_in_remote_dir(self, sftp, remote_dir):
 3         # 保存所有文件的列表
 4         all_files = list()
 5 
 6         # 去掉路徑字符串最后的字符'/',如果有的話
 7         if remote_dir[-1] == '/':
 8             remote_dir = remote_dir[0:-1]
 9 
10         # 獲取當前指定目錄下的所有目錄及文件,包含屬性值
11         files = sftp.listdir_attr(remote_dir)
12         for x in files:
13             # remote_dir目錄中每一個文件或目錄的完整路徑
14             filename = remote_dir + '/' + x.filename
15             # 如果是目錄,則遞歸處理該目錄,這里用到了stat庫中的S_ISDIR方法,與linux中的宏的名字完全一致
16             if S_ISDIR(x.st_mode):
17                 all_files.extend(self.__get_all_files_in_remote_dir(sftp, filename))
18             else:
19                 all_files.append(filename)
20         return all_files

  在上面的方法中,參數sftp表示已經建立的sftp連接,remote_dir是要掃描的遠端目錄。

  在掃描目錄的時候,使用的listdir_attr方法會列出指定目錄下的所有文件或目錄,並且還會列出其屬性,比如st_size,st_uid,st_gid,st_mode,st_atime,st_mtime,

  這些屬性與linux中的stat函數返回的屬性類似,我們就是根據其中的st_mode屬性來判斷是一個目錄還是文件,並且處理st_mode的方法(位於stat模塊中)也是與linux中定義的宏一致的。

  獲取到指定目錄下的所有文件之后,傳輸就比較簡單了,依次遍歷get即可:

 1     def sftp_get_dir(self, remote_dir, local_dir):
 2         t = paramiko.Transport(sock=(self.ip, 22))
 3         t.connect(username=self.username, password=self.password)
 4         sftp = paramiko.SFTPClient.from_transport(t)
 5 
 6         # 獲取遠端linux主機上指定目錄及其子目錄下的所有文件
 7         all_files = self.__get_all_files_in_remote_dir(sftp, remote_dir)
 8         # 依次get每一個文件
 9         for x in all_files:
10             filename = x.split('/')[-1]
11             local_filename = os.path.join(local_dir, filename)
12             print u'Get文件%s傳輸中...' % filename
13             sftp.get(x, local_filename)

  上面方法將remote_dir目錄中的所有文件都get到了本地local_dir目錄中,但是在本地沒有保持與遠端一致的目錄結構,只是簡單將所有文件保存在local_dir目錄中。

  如果要保持與遠端的目錄結構一致,就需要在本地ocal_dir中創建子目錄,這里暫且不講述了,如有這種需求可思考一下。

 

  下面再來看看put,其實與get幾乎一樣,現在掃描本地目錄,然后依次遍歷文件並put到遠端,

  由於是對本地目錄做掃描,因此不需要調用SFTP中的文件目錄處理接口了,直接使用python的os庫即可,代碼如下:

  

 1  # ------獲取本地指定目錄及其子目錄下的所有文件------
 2     def __get_all_files_in_local_dir(self, local_dir):
 3         # 保存所有文件的列表
 4         all_files = list()
 5 
 6         # 獲取當前指定目錄下的所有目錄及文件,包含屬性值
 7         files = os.listdir(local_dir)
 8         for x in files:
 9             # local_dir目錄中每一個文件或目錄的完整路徑
10             filename = os.path.join(local_dir, x)
11             # 如果是目錄,則遞歸處理該目錄
12             if os.path.isdir(x):
13                 all_files.extend(self.__get_all_files_in_local_dir(filename))
14             else:
15                 all_files.append(filename)
16         return all_files
17 
18     def sftp_put_dir(self, local_dir, remote_dir):
19         t = paramiko.Transport(sock=(self.ip, 22))
20         t.connect(username=self.username, password=self.password)
21         sftp = paramiko.SFTPClient.from_transport(t)
22 
23         # 去掉路徑字符穿最后的字符'/',如果有的話
24         if remote_dir[-1] == '/':
25             remote_dir = remote_dir[0:-1]
26 
27         # 獲取本地指定目錄及其子目錄下的所有文件
28         all_files = self.__get_all_files_in_local_dir(local_dir)
29         # 依次put每一個文件
30         for x in all_files:
31             filename = os.path.split(x)[-1]
32             remote_filename = remote_dir + '/' + filename
33             print u'Put文件%s傳輸中...' % filename
34             sftp.put(x, remote_filename)

  測試代碼如下:

 1 if __name__ == '__main__':
 2     remote_path = r'/home/sea'
 3     local_path = r'E:\PythonFiles\Learn\testsftp'
 4 
 5     host = Linux('192.168.180.128', 'root', '1234')
 6 
 7     # 將遠端remote_path目錄中的所有文件get到本地local_path目錄
 8     host.sftp_get_dir(remote_path, local_path)
 9     # # 將本地local_path目錄中的所有文件put到遠端remote_path目錄
10     # host.sftp_put_dir(remote_path, local_path)
11 
12 # 運行結果
13 Get文件.profile傳輸中...
14 Get文件.inputrc傳輸中...
15 Get文件.emacs傳輸中...
16 Get文件.bash_history傳輸中...
17 Get文件.bashrc傳輸中...

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM