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 fhqlongteng@163.com