FTP(文件傳輸協議),廣泛用於客戶端和服務器之間文件傳輸,SFTP即是加密了的FTP,即客戶端和服務器之間的文件傳輸採用的是加密數據數據傳輸,傳輸的底層數據使用SSL連接進行加密。
今天講解的這個程序實現的是把本地文件夾內的所有文件上傳到服務器,即實現了FTP軟件(FileZilla Client)的功能,這個程序會自動對比要上傳的本地文件夾和服務器上文件的數量,發現有新的文件就進行上傳。程序文件運行在windows系統上,創建一個計劃任務,就會自動定時的把本地文件上傳到服務器。
1、 sftp功能使用paramiko模塊,在命令行下,使用pip install paramiko安裝即可
2、程序的配置存儲在ftpcon.ini文件中,程序啓動時先加載配置文件,從配置文件中讀取上傳文件的目錄localdir=E:\python_test\hdrc_ac0,要上傳的文件類型fextern=.*, .*表示上傳所有文件,ftp用戶名。
3、使用paramiko上傳文件,直接調用相應的方法即可以實現,這種程序實現了一個重要的功能就是,使用遞歸調用的方法,分析本地文件與服務器文件的不同,不同就進行上傳。由sftp_upload_dir(self, sftp, local_dir, remote_dir):函數實現些功能。
sftp_upload_dir(self, sftp, local_dir, remote_dir):函數首先讀取本地目錄下面的文件和文件夾,返回文件列表和文件夾列表,同樣再讀取遠程服務器中的目錄下面的文件和文件夾,返回文件列表和文件夾列表。比較出只存在本地電腦上的文件或文件夾,當發現本地有新文件夾時在遠程服務器上面創建新文件夾,有新文件時,上傳新文件,並且把這些新增的文件上傳到服務器。通過多次循環遞歸調用實現了把本地文件夾內的文件及子目錄全部上傳到遠程服務器。下面是整個程序的代碼,修改其中的服務器地址,用戶名,密碼,就可以複製直接運行了
import paramiko
import os
import sys
from stat import *
import configparser
import datetime
# 定義一個類,表示一臺遠端linux主機
class Linux(object):
# 通過IP, 用戶名,密碼,超時時間初始化一個遠程Linux主機
def __init__(self, ip, username, password, fextern = '.*', timeout=30):
self.ip = ip
self.username = username
self.password = password
self.timeout = timeout
self.fextern = fextern
# transport和chanel
self.t = ''
self.chan = ''
# 鏈接失敗的重試次數
self.try_times = 3
#設置輸出打印到文件
self.fp = open('log.txt', 'a+')
#錯誤輸出到文件
sys.stderr = self.fp
sys.stdout = self.fp
print(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
# get單個文件
def sftp_get(self, remotefile, localfile):
t = paramiko.Transport(sock=(self.ip, 22))
t.connect(username=self.username, password=self.password)
sftp = paramiko.SFTPClient.from_transport(t)
sftp.get(remotefile, localfile)
t.close()
# put單個文件
def sftp_put(self, localfile, remotefile):
t = paramiko.Transport(sock=(self.ip, 22))
t.connect(username=self.username, password=self.password)
sftp = paramiko.SFTPClient.from_transport(t)
sftp.put(localfile, remotefile)
t.close()
print('ok')
# ------獲取遠端linux主機上指定目錄及其子目錄下的所有文件------
def __get_all_files_in_remote_dir(self, sftp, remote_dir):
# 保存所有文件的列表
all_files = []
all_dirs = []
# 去掉路徑字符串最後的字符'/',如果有的話
if remote_dir[-1] == '/':
remote_dir = remote_dir[0:-1]
# 獲取當前指定目錄下的所有目錄及文件,包含屬性值
files = sftp.listdir_attr(remote_dir)
for x in files:
# remote_dir目錄中每一個文件或目錄的完整路徑
filename = remote_dir + '/' + x.filename
# 如果是目錄,則遞歸處理該目錄,這裏用到了stat庫中的S_ISDIR方法,與linux中的宏的名字完全一致
if S_ISDIR(x.st_mode):
#all_files.extend(self.__get_all_files_in_remote_dir(sftp, filename))
all_dirs.append(x.filename)
else:
#只返回文件名稱,不包含路徑
all_files.append(x.filename)
return all_files, all_dirs
# ------獲取本地指定目錄及其子目錄下的所有文件------
def __get_all_files_in_local_dir(self, local_dir):
# 保存所有文件的列表
all_files = []
all_dirs = []
# 獲取當前指定目錄下的所有目錄及文件,包含屬性值
files = os.listdir(local_dir)
for x in files:
# local_dir目錄中每一個文件或目錄的完整路徑
filename = os.path.join(local_dir, x)
# 如果是目錄,則遞歸處理該目錄
if os.path.isdir(filename):
#返回目錄列表
all_dirs.append(x)
else:
#返回文件列表
all_files.append(x)
return all_files,all_dirs
#比較afiles目錄中比bfiles中多的文件
def __dirCompare(self, afiles, bfiles):
setA = set(afiles)
setB = set(bfiles)
# 處理僅出現在一個目錄中的文件
onlyFiles = setA ^ setB
onlyInA = []
onlyInB = []
for of in onlyFiles:
if of in afiles:
onlyInA.append(of)
elif of in bfiles:
onlyInB.append(of)
return onlyInA
#把本機文件夾中的文件上傳到遠程服務器中去
def sftp_upload_dir(self, sftp, local_dir, remote_dir):
#獲取遠程服務器目錄下的文件列表
dst_files, dst_dirs = self.__get_all_files_in_remote_dir(sftp, remote_dir)
#獲取本地指定目錄及其子目錄下的所有文件
src_files, src_dirs = self.__get_all_files_in_local_dir(local_dir)
#獲取不同文件
dif_file = self.__dirCompare(src_files, dst_files)
dif_dir = self.__dirCompare(src_dirs, dst_dirs)
#上傳不同的文件
#在服務器目錄下創建沒有的目錄
for d in dif_dir:
#在服務器上面創建新目錄
sftp.mkdir(remote_dir + '/' + d)
#遞歸處理下一級目錄下面的文件
for d in src_dirs:
self.sftp_upload_dir(sftp, local_dir + '\\' + d, remote_dir + '/' + d)
#向服務器上傳文件
for f in dif_file:
remote_filename = remote_dir + '/' + f
local_filename = local_dir + '\\' + f
#print(local_filename)
externName = os.path.splitext(local_filename)[1]
if ((self.fextern == '.*') or (externName == self.fextern)):
print('文件%s上傳成功'%local_filename)
sftp.put(local_filename, remote_filename)
def sftp_put_dir(self, local_dir, remote_dir):
try:
t = paramiko.Transport(sock=(self.ip, 22))
t.connect(username=self.username, password=self.password)
except:
print('Connect Error!')
self.fp.close()
return
sftp = paramiko.SFTPClient.from_transport(t)
#把本地文件夾中的文件及子目錄中的文件全部上傳到服務器
self.sftp_upload_dir(sftp, local_dir, remote_dir)
print('Finish')
self.fp.close()
if __name__ == '__main__':
remotedir = r'/var/ftp/pub/T****'
ftp_host = 'www.rainbow****.com'
#讀取配置文件
config = configparser.ConfigParser()
try:
config.read('ftpcon.ini')
ftp_user = config.get('FTP', 'ftp_user')
ftp_extern = config.get('FTP', 'fextern')
localdir = config.get('FTP', 'localdir')
except:
ftp_user = 'ftpadmi**'
ftp_extern = '.*'
localdir = r'E:\\python_test\\ftp_up_data'
print('No ftpcon.ini')
host = Linux(ftp_host, ftp_user, '******', ftp_extern)
# # 將本地的xxoo.txt put到遠端,並保持爲xxoo.txt
host.sftp_put_dir(localdir, remotedir)
4、擴展功能,本程序再加修改,增加比較本地服務器與遠程服務器上同樣文件的內容不同,就可以實現整個文件夾上傳的功能,並且實現了文件同步的功能,自動同步修改的文件
作者:龍騰
283669063 [email protected]