# SFTP連線 ###### tags: `python` `ftp` `sftp` ## 準備 #### 安裝pysftp模組 ```bash # pip3 install pysftp ``` ## 常用方法 | 方法名稱 | 說明 | 範例 | | -------- | -------- | ---| | pwd | 目前路徑 |`print(sftp.pwd)`| | cwd() | 移動目錄 |`sftp.cwd('/test')`| | list_dir() | 取得目錄下所有檔案和目錄(僅名稱) |`sftp.list_dir()`| | listdir_attr() | 取得目錄下所有檔案和目錄(含屬性) |`sftp.listdir_attr()`| | exists() | 判斷檔案或目錄是否存在 |`sftp.exists('hello')`| | mkdir() | 建立新的目錄 |`sftp.mkdir('hello')`| | put() | 上傳檔案到目前路徑 |`sftp.put('a.txt', 'c.txt')`| | get() | 下載目前路徑下的檔案 |`sftp.get('a.txt', 'b.txt')`| | isdir() | 判斷檔案是否為目錄 |`sftp.isdir('hello')`| | isfile() | 判斷檔案是否為檔案 |`sftp.isfile('a.txt')`| >**補充** > >- sftp為已連線的物件 ## 說明 SFTP Server連線時會檢查HostKey,如果沒有提供的話會出現錯誤而造成無法連線, ``` AttributeError: 'Connection' object has no attribute '_sftp_live' ``` 有幾種解決方式如下: #### 方式一:在`~/.ssh/known_hosts`加入Public Key 先使用`ssh-keyscan`工具掃描該站台的Public Key ``` $ ssh-keyscan example.com example.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDj4CkA3SdT7TiPqHKMOa1u... ``` 然後再將掃描得到的Public Key加入到`~/.ssh/known_hosts`檔案中 ``` example.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDj4CkA3SdT7TiPqHKMO... ``` 在Python內加入下面程式碼: ```python= cnopts = pysftp.CnOpts(knownhosts='known_hosts') with pysftp.Connection(host, username, password, cnopts=cnopts) as sftp: ... ``` #### 方式二:直接在程式碼裡面指定Public Key ```python= keydata = b"""AAAAB3NzaC1yc2EAAAADAQABAAAAgQDj4CkA3SdT7TiPqHKMO...""" key = paramiko.RSAKey(data=decodebytes(keydata)) cnopts = pysftp.CnOpts() cnopts.hostkeys.add(host, 'ssh-rsa', key) with pysftp.Connection(host, username, password, cnopts=cnopts) as sftp: ... ``` #### 方式三:第一次連線忽略Public Key,連線後再將Server傳過來的Key儲存下來 ```python= if cnopts.hostkeys.lookup(host) == None: print("發現新的SFTP站台,將允許該站台憑證 .....") # 將新的憑證存到變數裡 hostkeys = cnopts.hostkeys print(hostkeys.values()) # 將憑證檢查暫時關閉,避免第一次無法連線, cnopts.hostkeys = None with pysftp.Connection(host, username=username, password=password, cnopts=cnopts) as sftp: # 加入新的憑證 if hostkeys != None: print("連線到該新的SFTP站台並儲存該站台憑證") hostkeys.add(host, sftp.remote_server_key.get_name(), sftp.remote_server_key) hostkeys.save(pysftp.helpers.known_hosts()) ``` > **補充:** > > 如果該server並非使用標準的port 22,可以在`pysftp.Connection()`方法加上`port=21`參數來指定port。 ## 範例程式(使用方式三) ```python= import pysftp host = '??????????????' username = '?????????????' password = '???????????' cnopts = pysftp.CnOpts() hostkeys = None if cnopts.hostkeys.lookup(host) == None: print("發現新的SFTP站台,將允許該站台憑證 .....") # 將新的憑證存到變數裡 hostkeys = cnopts.hostkeys # 將憑證檢查暫時關閉,避免第一次無法連線, cnopts.hostkeys = None with pysftp.Connection(host, username=username, password=password, cnopts=cnopts) as sftp: # 加入新的憑證 if hostkeys != None: print("連線到該新的SFTP站台並儲存該站台憑證") hostkeys.add(host, sftp.remote_server_key.get_name(), sftp.remote_server_key) hostkeys.save(pysftp.helpers.known_hosts()) # 切換路徑 sftp.cwd('/aaron-test') # 目前所在路徑 print(sftp.pwd) # 取得目錄詳細內容 directory = sftp.listdir_attr() # 印出結果 for attr in directory: print(attr.filename, attr, sftp.isdir(attr.filename)) # 建立目錄 if not sftp.exists('hello'): sftp.mkdir('hello') # 上傳檔案 sftp.put('a.txt', 'c.txt') # 下載檔案 sftp.get('c.txt', 'b.txt') # 取得目錄所有內容 print(sftp.listdir()) ``` ## 練習 1. 寫一sftp程式,可以顯示該目錄下的所有目錄和檔案,以及改目錄下的子目錄內所有內容 2. 寫一sftp程式,可以下載該目錄下的所有檔案