IT@程序员猿媛Python小程序

python——ftp服务器指定日期所有文件的更新下载

2019-02-20  本文已影响163人  末一哟

盼望着盼望着,终于迎来了一篇原创(老泪纵横)。


也是任务需求,花了两天弄了一下,现在总算是满足要求了。可能容错性还不够,冗余度应该还行。但是写过了才有体会,是真的痛苦。里面的小弯弯逻辑是头疼。下面就来说一下实现的功能:

大前提:在规定的文件格式和路径规则下

实现: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来替代。
但需要说一下的是,这里的逻辑,懂的人可能能从我的程序里面看出来,也不是很特别复杂,但还是有些绕的。所以如果你也想自己尝试一下的话,最好先按照我的来跑一遍。跑通了,再自己飞~

给两个效果图(gif9.62MB,加载慢直接看图或无视): 程序演示 对整个FTP服务器的监听 只更新指定日期下文件

这里在说一下win7上ftp服务器的搭建,戳这里
搭建需注意如下几点:


我们没能力发现知识,我们只是知识的寄生虫

上一篇下一篇

猜你喜欢

热点阅读