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

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


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

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

实现: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服务器的搭建,戳这里
搭建需注意如下几点:

  • 请把FTP服务器和Web管理工具的所有子文件选项打钩(涂满深色表示未全选,不行滴)
    FTP服务器和Web管理工具全选√
  • 添加网址IP地址的时候,可下拉选择可用IP中当前电脑查询得到的IP

    (cmd下ipconfig的IPV4地址)
    添加网站使用的IP
  • 添加FTP站点时的IP同上选择
    添加FTP站点的IP
    注意,端口最好改一下,虽然全网默认21.
    最后创建发布完成后,浏览器输入:
    ftp://192.168.1.100:2121+用户名和密码
    访问成功即创建成功!
    还有一个使用的注意点:访问成功后,如果您使用的局域网路由器网线,那么您的这个浏览器登录FTP服务器的方法,只能在你本机操作,如果想让别的电脑也通过浏览器访问的话,需要在192.168.1.1上对路由器的对外IP进行统一设置才行。否则外界读取到的IP并非你本机IP的矛盾会暴露出来的哦。但是通过命令行或程序,比如上文的python的ftplib库函数进行ftp服务器的登录连接,是没问题的。(可能是因为不涉及浏览器上NDS的问题)

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容

  • FTP服务概述 简单FTP构建及访问 VSFTP服务基础 用户禁锢、黑白名单 FTP并发及带宽限制 一、FTP服务...
    紫_轩阅读 7,586评论 3 25
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,934评论 6 13
  • 1、第八章 Samba服务器2、第八章 NFS服务器3、第十章 Linux下DNS服务器配站点,域名解析概念命令:...
    哈熝少主阅读 3,719评论 0 10
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,066评论 4 62
  • 前一秒还在感慨陪伴了整个少年时光的李咏,下一秒是打开我武侠大门的金庸,这个十月真是冷血残酷。 原本微博发了感慨,还...
    慢半拍shining阅读 647评论 0 1