盼望着盼望着,终于迎来了一篇原创(老泪纵横)。
也是任务需求,花了两天弄了一下,现在总算是满足要求了。可能容错性还不够,冗余度应该还行。但是写过了才有体会,是真的痛苦。里面的小弯弯逻辑是头疼。下面就来说一下实现的功能:
大前提:在规定的文件格式和路径规则下
实现:ftp服务器上指定日期下的所有文件的更新下载
说明:
1.大前提,就是我项目要求的文件存放格式,因为有这个才能写出一键自动化的程序,否则。。。应该是痴人说梦(恕我才疏学浅吧)2.具体实现的功能由如下知识点拼凑:python库ftplib的使用;os库的使用(本地文件操作);针对归类规则思考的程序逻辑结构(这才是最难的,一点一点摸索出来的)等。
3.这里的指定日期下可不止一处日期哦,比如传感器A下有2019年1月1日,传感器B也有这个日期,那么该模块的功能是同时更新A与B下的指定日期的文件。怎么样,是不是挺工程化的。
下面贴代码:
# -*- coding: utf-8 -*-
"""
Created on Wed Feb 20 16:56:26 2019
@author: Raul
"""
import ftplib
import os
import datetime
## 创建地址文件 创建成功返回true 已存在返回false
def mkdir_ifnotExist(path):
path = path.strip() # 删除地址首尾空格
path = path.rstrip("\\") # 保留\\之后的内容 以空格结尾
isExist = os.path.exists(path)
if not isExist :
os.makedirs(path)
print("%s did not exist.\nNow is created."%path)
return True
else :
print("%s has exist!"%path)
return False
## ftp连接函数
def ftpconnect(ftpserver,port,username,password):
ftp = ftplib.FTP()
try:
ftp.connect(ftpserver,port)
except:
raise(IOError('FTP connect failed!'))
try:
ftp.login(username,password)
except:
raise(IOError("FTP login failed!"))
else:
print("********* ftp连接、登录成功!*********")
# 中文乱码问题
ftp.encoding = 'GB18030'
return ftp
## ftp文件下载函数
def ftpdownload(ftp,local_path,ftp_path,filename,bufsize = 1024*10):
# 进入下载路径
ftp.cwd(ftp_path)
print("成功进入目录:",ftp.pwd().encode('iso-8859-1').decode('gbk'),"\n下载文件:",filename) # 将GB18030转换成UFT可以显示的格式
save_path = local_path + ftp_path
# 调用地址创建函数
mkdir_ifnotExist(save_path)
save_path = save_path + "/" + filename
fp = open(save_path,'wb')
#print("打开本地保存文件...")
ftp.retrbinary('RETR %s' % filename, fp.write, bufsize)
#print("写入文件成功!")
fp.close()
## ftp退出连接函数
def ftpquit(ftp):
try:
ftp.quit()
except:
raise(IOError("FTP quit failed!"))
else:
print("*********ftp已断开连接!*********")
## 拷贝ftp服务器指定日期所有文件函数
def copy_new_file(ftp):
nlst = ftp.nlst()
for name in nlst:
# 调用具体实现功能函数 该函数为自迭代函数
find_assign_file(ftp,name,0)
# 逻辑需要 服务器退出到根目录下
while ftp.nlst() != nlst:
ftp.cwd("..")
## 查找指定文件自迭代函数
def find_assign_file(ftp,file_name,year_flag):
# year_flag: 当当前路径为指定年份时为1,否则为0
folder_flag = 1 # folder_flag 0:非文件夹 1:文件夹
cmd_count = 0 # 目录进入层数记录
global detectionfolder_list # 每个测点上的节点目录
global tmp_file # 存放每个日期文件下的文件名列表 [[node1],[node2],...,[nodeN]]
global record # tmp_file的索引 使用:tmp_file[recor]
global date_flag # 0:非指定日期;1:指定日期;2:为当前指定日期的检测节点文件夹;3:保存指定日期文件夹中的文件
try:
ftp.cwd(file_name) #需要判断的元素
ftp.cwd("..") #如果能通过路劲打开必为文件夹,在此返回上一级
# 不能通过路径打开必为文件,抓取其错误信息
except ftplib.error_perm as fe:
folder_flag = 0 # 发现文件txt
if date_flag == 2: # 父文件为节点文件夹
date_flag = 3 # 下载标志
finally:
if folder_flag == 1: # 要访问的是文件夹
father_file_list = ftp.nlst() # 获取当前路径下所有文件夹
# 逐个访问该路径下所有文件夹
for father_name in father_file_list:
try:
ftp.cwd(father_name)
# 若当前路径下不为空
if ftp.nlst() != []:
# 逐个访问其子文件夹
for child_name in ftp.nlst():
# 获取节点文件名
try:
ftp.cwd(child_name)
cmd_count = 1
ftp.cwd(ftp.nlst()[0])
cmd_count = 2
ftp.cwd(ftp.nlst()[0])
cmd_count = 3
ftp.cwd(ftp.nlst()[0])
cmd_count = 4
# 操作正常返回到原路径
ftp.cwd("..")
ftp.cwd("..")
ftp.cwd("..")
ftp.cwd("..")
except (ftplib.error_perm,IndexError) as fe:
if cmd_count == 3:
ftp.cwd("..")
detectionfolder_list = ftp.nlst()
ftp.cwd("..")
ftp.cwd("..")
else:
while cmd_count > 0 :
ftp.cwd("..")
cmd_count -= 1
pass
# 当且仅当date_flag == 1(符合查找日期要求)且 父文件为节点文件夹(子文件夹则为输出文件) 进入输出文件名模块
if date_flag == 1 and (father_name in detectionfolder_list):
date_flag = 2 # 查询成功
else:
# 当前子文件不是节点文件夹时
if (child_name not in detectionfolder_list):
# 如果年份满足查询要求,令年份标志为1
if (father_name == "2019年"): #"""or father_name == "1月" """): and (child_name == "1月" """or child_name == "1日" """)):
year_flag = 1 # 年份标志为避免差错年份,比如查找2019年1月1日,如不添加其,则也会查找到2018年1月1日
# 如果年份标志为1,且月、日也满足查询要求,此时令date_flag=1,即查询到指定日期
if year_flag == 1 and father_name == "1月" and child_name=="1日":
date_flag = 1
else:
date_flag = 0
# 当前子文件是节点文件但父文件不符合查询日期
elif father_name != "1日":
date_flag = 0
# 进入子文件迭代查询
find_assign_file(ftp,child_name,year_flag)
# 查询完成后退回原路径
ftp.cwd("..")
# 只有当找到文件才会返回
# 一个节点文件夹进行一次查询即可(因为节点文件夹中都是txt)
break
# 子文件的路径下为空,返回到父路径
else:
ftp.cwd("..")
# 父路径为空,跳过
except ftplib.error_perm as er:
pass
# 访问的是文件txt
else:
if date_flag == 3: # 3是符合读取要求的标志
# 令日期下文件夹数加1
tmp_file.append([])
for name in ftp.nlst():
# 如果当前路径下的文件不在上一时刻该文件夹下 则下载
if name not in tmp_file[record]:
t = ftp.pwd().encode('iso-8859-1').decode('gbk')
ftpdownload(ftp,save_path + t,"",name)
# 更新该文件夹下文件内容
tmp_file[record] += name
date_flag = 1 # 重置查找日期标志为1,检查下一节点文件夹
record += 1 # 日期文件计数+1,跟着程序进入下一符合查询日期的日期文件夹下,这相当于:tmp_file=[[第一个符合查询要求的文件夹],[第二个符合查询要求的文件夹],...,[record]]
# 我们根据查询顺序默认相同的原理,记录一个随查找自增长的序号以索引符合要求的查找文件夹
## RUN
# 固定参数信息
ftpserver = '192.168.1.100'
port = 2122
username = 'raul'
password = '123321'
save_path = "C:/Users/Administrator/Desktop/桥梁备份数据"
# 全局变量
detectionfolder_list = [] # 每个测点上的节点目录
tmp_file = [] # 存放每个日期文件下的文件名列表 [[node1],[node2],...,[nodeN]]
date_flag = 0 # 0:非指定日期;1:指定日期;2:为当前指定日期的检测节点文件夹;3:保存指定日期文件夹中的文件
record = 0 # tmp_file的索引 使用:tmp_file[recor]
# 运行结构
# 1.连接ftp服务器
ftp = ftpconnect(ftpserver,port,username,password)
# 2.拷贝新文件 ps:这里还需改动,即将程序中的 年-月-日要求改为具体要求,即当时时间的日期datetime.datetime.now().year/month/day
copy_new_file(ftp)
# 3.断开ftp服务器连接
ftpquit(ftp)
函数各个模块写得还算清晰(无尽的写+一点点强迫症),注释也写的算完整的。需要注意的是这里我用于测试的,日期给的2019年1月1日,如果真的放到系统中实时更新,应调用datetime.datetime.now().year/month/day来替代。
但需要说一下的是,这里的逻辑,懂的人可能能从我的程序里面看出来,也不是很特别复杂,但还是有些绕的。所以如果你也想自己尝试一下的话,最好先按照我的来跑一遍。跑通了,再自己飞~
这里在说一下win7上ftp服务器的搭建,戳这里。
搭建需注意如下几点:
-
请把FTP服务器和Web管理工具的所有子文件选项打钩(涂满深色表示未全选,不行滴)
-
添加网址IP地址的时候,可下拉选择可用IP中当前电脑查询得到的IP
(cmd下ipconfig的IPV4地址)
- 添加FTP站点时的IP同上选择
最后创建发布完成后,浏览器输入:
ftp://192.168.1.100:2121+用户名和密码
访问成功即创建成功!
还有一个使用的注意点:访问成功后,如果您使用的局域网路由器网线,那么您的这个浏览器登录FTP服务器的方法,只能在你本机操作,如果想让别的电脑也通过浏览器访问的话,需要在192.168.1.1上对路由器的对外IP进行统一设置才行。否则外界读取到的IP并非你本机IP的矛盾会暴露出来的哦。但是通过命令行或程序,比如上文的python的ftplib库函数进行ftp服务器的登录连接,是没问题的。(可能是因为不涉及浏览器上NDS的问题)