背景:最近工作有需要自动化FTP上传下载数据并进行对比的功能,本来是打算直接在网上搜现成的来改一下的,但是整合完发现,一次性下载超过200多个文件的时候就会报错,而且看别人写的代码是一件痛苦的事情,所以就干脆搜了点资料自己写了,现在记个笔记,有需要的可以拿现成的用。
目录
ftplib模块的一些基本的东西:
OS模块的一些操作:
自己的实现部分:
ftplib模块的一些基本的东西:
from ftplib import FTP # 导入FTP模块
ftp = FTP() # 设置变量
ftp.set_debuglevle(2) # 设置实例的调试级别,0:不输出(缺省值),1:中等调试输出,通常每个请求一行,2:显示详细信息
ftp.connect(host='', port=0) # 连接到给定的主机和端口(默认21),很少需要指定其他端口号。
ftp.getwelcome() # 打印出欢迎信息
ftp.login('user', 'password') # 以用户身份登录
ftp.abort() # 中止正在进行的文件传输
ftp.retrbinary("RETR filename", fp, blocksize) # 接受服务器上的文件并写入本地文件
ftp.storbinary("STOR filename", fp, blocksize) # 以二进制传输模式存储文件,即上传文件至FTP服务器
ftp.set_pasv() # 设置模式, true为被动模式(默认值),false为主动模式
ftp.quit() # 退出ftp
ftp.cwd(pathname) # 设置FTP当前操作的路径
ftp.dir() # 显示目录下所有目录信息
ftp.nlist() # 获取目录下的文件
ftp.mkd(pathname) # 新建远程目录
ftp.pwd() # 返回当前所在位置
ftp.rmd(dirname) # 删除远程目录
ftp.delete(filename) # 删除远程文件
ftp.rename(fromname, toname) # 将fromname修改为toname
ftp.size(filename) # 请求服务器上名为filename的文件的大小
OS模块的一些操作:
方法说明
os.mkdir创建目录
os.rmdir删除目录
os.rename重命名
os.remove删除文件
os.getcwd获取当前工作路径
os.walk遍历目录
os.path.join连接目录与文件名
os.path.split分割文件名与目录
os.path.abspath获取绝对路径
os.path.dirname获取路径
os.path.basename获取文件名或文件夹名
os.path.splitext分离文件名与扩展名
os.path.isfile判断给出的路径是否是一个文件
os.path.isdir判断给出的路径是否是一个目录
自己的实现部分:
#! /usr/bin/python # -*- coding: utf-8 -*
import re
import sys
import datetime, time
from ftplib import FTP # 定义了FTP类,实现ftp上传和下载
import logging
import os
"""
V1.0:
初步实现功能
V1.1:
目录下载方式进行优化,目录下存在目录的话,也会将该目录下的文件进行下载
进行文件对比的优化,进行对比的目录中存在目录的话,也会将该目录下的文件进行对比
V1.2:
实现整个目录上传(目录中包含目录也可以)
##待验证##
备注:
Linux系统,所下载的文件权限(其他用户权限)没有读权限时会报错:ftplib.error_perm: 550 Failed to open file.
-rw-rw---- 1 ftp 1000 291151872 Mar 29 20:55 sj1.dat #无法下载
-rw-rw-rw- 1 ftp 1000 11829248 Mar 29 20:55 sj2.dat #可下载
-rw------- 1 ftp 1000 17743872 Mar 29 20:55 sj3.dat #无法下载
从左至右:-rwxrwxrwx
最左侧1位d表示文件夹,l表示连接文件,-表示文件
2-4位数字代表文件所有者的权限
5-7位数字代表同组用户的权限
8-10数字代表其他用户的权限
权限命令:chmod 777 文件名
无权限(-)=0,读(r)=4,写(w)=2,执行(x)=1,例如:读+写+执行=4+2+1=7
"""
class MyFTP:
"""
ftp自动下载、自动上传脚本,可以递归目录操作
"""
def __init__(self):
self.log()
def FTP_register(self, host, port=21, username=None, password=None):
"""
”初始化 FTP 客户端"
:param host: 服务器ip地址
:param port: 端口号
:param username: 用户名
:param password: 密码
:return:
"""
# self.logger.info("__init__()---> host = %s ,port = %s ,username=%s ,password = %s ," % (host, port, username, password))
self.ftp = FTP()
self.ftp.set_debuglevel(2)
self.ftp.connect(host, port)
self.ftp.login(username, password)
# 设置下编码方式、主\被动模式、缓冲区大小,打印欢迎信息
self.ftp.encoding = 'gbk'
self.ftp.set_pasv(True)
self.BLOCKSIZE = 8192
self.logger.info(self.ftp.getwelcome())
def log(self):
"""
创建日志器
设置日志打印级别
创建一个handler,用于写入日志文件
mode = "a":追加,“w”:覆盖
"""
self.logger = logging.getLogger()
self.logger.setLevel(logging.INFO)
fh = logging.FileHandler(filename="log.log", mode='w', encoding='utf-8') # 指定utf-8格式编码,避免输出的日志文本乱码
fh.setLevel(logging.DEBUG)
#创建一个handler,用于将日志输出到控制台
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
# 定义handler的输出格式
formatter = logging.Formatter('%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
fh.setFormatter(formatter)
ch.setFormatter(formatter)
# 给logger添加handler
self.logger.addHandler(fh)
self.logger.addHandler(ch)
def FILE_Compare(self, path_0, path_1):
'''
进行文件内容比较
'''
with open(path_0, 'rb') as file_byte:
a = file_byte.read()
with open(path_1, 'rb') as file_byte1:
b = file_byte1.read()
if a == b:
self.logger.debug('内容对比:YES-->:[%s<--->%s]' % (path_0, path_1))
else:
self.logger.info("内容对比:NO-->:[%s<--->%s]" % (path_0, path_1))
return
def Obtain_Directory_list(self, path, a=1):
"""
“进入指定列表并获取列表下所有文件信息”
:param remote_dir: 远程路径
:return: 输出目录下文件列表
:param a: a==0:获取远程路径列表
a==1:获取本地路径列表
:return:
"""
if a == 0:
self.ftp.cwd(path) #进入远程路径
return self.ftp.nlst()#获取路径下文件名
elif a == 1:
return os.listdir(path)#获取本地路径下文件名
def FTP_Download_File(self, local_dir, remote_dir):
"""
下载单个文件
:param local_dir:本地路径
:param remote_dir:远程路径
:return:
"""
with open(local_dir, "wb") as f:
try:
self.ftp.retrbinary("RETR %s" % remote_dir, f.write, self.BLOCKSIZE)
except Exception as result:
self.logger.warning("下载文件%s时发生错误 % s----->可能是文件无读写权限" % (remote_dir, result))
return
return()
def FTP_Upload_File(self, local_dir, remote_dir):
"""
上传单个文件
:param local_dir:本地路径
:param remote_dir:远程路径
:return:
"""
with open(local_dir, "rb") as f:
self.ftp.storbinary("STOR %s" % remote_dir, f, self.BLOCKSIZE)
return ()
def FTP_Download_Directory(self, local_dir, remote_dir):
# """
# "下载整个目录"
# :param local_dir:
# :param remote_dir:
# :return:
# """
# local_dir = local_dir
# List = self.Obtain_Directory_list(remote_dir, a=0)
#
# for a in List:
# self.logger.info(local_dir + a + "----kaisi--->" + remote_dir + "/" + a)
# self.FTP_Download_File(str(local_dir + a), str(remote_dir + "/" + a))
#
return print("该函数已经弃用")
def FTP_Upload_Directory(self, local_dir, remote_dir):
"""
“上传整个目录”
:param local_dir:
:param remote_dir:
:return:
"""
local_dir = local_dir
List = self.Obtain_Directory_list(local_dir, a=1)
for a in List:
self.logger.debug(local_dir + a + "------->" + remote_dir + "/" + a)
self.FTP_Upload_File(str(local_dir + "/" + a), str(remote_dir + a))
return
def FILE_Compare_EXECUTE(self, path_0, path_1):
"""
“对目录文件进行比较”
:param path_0: 第一个目录路径
:param path_1: 第二个目录路径
:return:
列表比较方式
a = [1,0,2] b = [1,0,0]
x = [k for k in a if k in b] x = [1,0]
x = [k for k in a if k not in b] x = [2]
"""
path_0_list = self.Obtain_Directory_list(path_0, a=1)
path_1_list = self.Obtain_Directory_list(path_1, a=1)
x = [k for k in path_0_list if k not in path_1_list]
y = [k for k in path_1_list if k not in path_0_list]
z = [k for k in path_0_list if k in path_1_list]
if x or y:
self.logger.info("丨———————————————↓———————————————————丨")
self.logger.info("目录" + path_0 + "缺少>>:" + str(y))
self.logger.info("目录" + path_1 + "缺少>>:" + str(x))
self.logger.info("丨———————————————↑———————————————————丨")
else:
self.logger.debug(">----两个目录文件个数一致,无缺失<----")
for i in z:
#判断路径是否为目录,是则进入路径,不是则进行文件比较
if os.path.isdir(path_0 + i) == True:
logging.debug("转到目录:--->" + path_0 + i + "/" + "与" + path_1 + i + "/")
self.FILE_Compare_EXECUTE(path_0 + i + "/", path_1 + i + "/")
else:
self.FILE_Compare(path_0 + i, path_1 + i)
def get_file_name(self, line):
""" 获取远程路径目录下的文件名
pos = line.rfind(' '):匹配字符串最后一次出现的地方
参数:
line:
"""
pos = line.rfind(' ')
while (line[pos] != ' '):
pos += 1
while (line[pos] == ' '):
pos += 1
file_arr = [line[0], line[pos:]]
return file_arr
def FTP_Download_Directory_duo_Directory(self, local_dir, remote_dir):
"""
"下载整个目录"
:param local_dir:本地路径
:param remote_dir:远程路径
:return:
"""
local_dir = local_dir
remote_dir = remote_dir + "/"
self.ftp.cwd(remote_dir) # 进入远程路径
dir_list = []
self.ftp.dir('.', dir_list.append) #获取本地文件
list_list = []
for i in dir_list:
list_list.append(self.get_file_name(i))
self.logger.debug(list_list)
for a in list_list:
# 判断是否为目录
if a[0] == "d":
#判断本地是否存在该目录,没有则创建
if os.path.exists(local_dir + a[1]) == False:
os.mkdir(local_dir + a[1])
self.logger.debug(local_dir + "/" + a[1] + "/" + "------建文件夹并进入------" + remote_dir + a[1])
#递归进入下一路径
self.FTP_Download_Directory_duo_Directory(local_dir + a[1] + "/", remote_dir + a[1])
elif a[0] == "-":
self.logger.info(str(local_dir + a[1]) + "-------下载文件-----" + str(remote_dir + a[1]))
self.FTP_Download_File(str(local_dir + a[1]), str(remote_dir + "/" + a[1]))
def FTP_Upload_Directory_duo_Directory(self, local_dir, remote_dir):
"""
“上传整个目录”
:param local_dir:
:param remote_dir:
:return:
"""
local_dir = local_dir
remote_dir = remote_dir + "/"
# 进入远程路径
self.ftp.cwd(remote_dir)
list_1 = os.listdir(local_dir)
for a in list_1:
b = os.path.join(local_dir, a)
#判断是否为文件夹
if os.path.isdir(b):
dir_list = []
self.ftp.dir('.', dir_list.append) # 获取远程路径目录下的文件名
list_list = []
for i in dir_list:
list_list.append(self.get_file_name(i))
self.logger.debug(list_list)
# 校验
jiao_yan = 0
for e in list_list:
if e[0] == "d" and e[1] == a:
jiao_yan = 1
self.FTP_Upload_Directory_duo_Directory(local_dir + "/" + a + "/", remote_dir + a)
if jiao_yan == 0:
#创建远程目录
self.ftp.mkd(remote_dir + a)
self.logger.debug(local_dir + "/" + a + "/" + "------建文件夹并进入------" + remote_dir + a)
self.FTP_Upload_Directory_duo_Directory(local_dir + "/" + a + "/", remote_dir + a)
else:
self.logger.info(local_dir + a + "-------上传文件-----" + remote_dir + a)
# print(local_dir + a,remote_dir + a)
self.FTP_Upload_File(local_dir + a, remote_dir + a)
return
def Delete_File(self, vremote_path, TYPE=0):
"""
删除文件或目录
:param vremote_path:
:param TYPE: 0——目录,1——文件
:return:
"""
if TYPE == 0:
if os.path.exists(vremote_path) == False:
self.ftp.rmd(vremote_path) # 删除远程目录
elif TYPE == 1:
if os.path.exists(vremote_path) == False:
self.ftp.delete(vremote_path) # 删除远程文件
return
if __name__ == '__main__':
My_FTP = MyFTP()
My_FTP.FTP_register(host="10.0.1.4")
My_FTP.FTP_Upload_Directory_duo_Directory("H:/vib/", "/LH_DATA/hdd/clb")
# # My_FTP.FILE_Compare_EXECUTE("H:/1/", "H:/2/")
# My_FTP.FTP_Upload_File("'H:/vib/014445/H:/vib/014445/motor_shock_20220806014445.dat'", "/LH_DATA/hdd/log/200001010034-vib.dat")
#
My_FTP.ftp.close()#退出FTP
'''
使用示例
下载单个文件
My_FTP.FTP_Download_File("H:/200001010034-vib.dat", "/LH_DATA/hdd/clb/raw/20000101/200001010034-vib.dat")
上传单个文件
My_FTP.FTP_Upload_File("H:/200001010034-vib.dat","/LH_DATA/hdd/log/200001010034-vib.dat")
下载目录:目录中不能有目录,不然可能会报错--------->(弃用,已经被注释)
My_FTP.FTP_Download_Directory("H:/2/", "/LH_DATA/hdd/clb/raw/20000101")
下载整个目录:目录中有目录也可下载
My_FTP.FTP_Download_Directory_duo_Directory("H:/vib/", "/motor/motor/20220806")
上传目录:目录中不能有目录,不然可能会报错
My_FTP.FTP_Upload_Directory("H:/2/","/LH_DATA/hdd/log/")
目录文件对比:目录中不能有目录,不然可能会报错
My_FTP.FILE_Compare_EXECUTE("H:/1/", "H:/2/")
'''