Python实现FTP上传本地文件和目录

最近在做一个稽核任务,需要FTP登录服务器下载文件到本地和上传文件到服务器,参考了不少例子,功能都太单一,很少能直接拿来使用,于是自己写了一个。FTP下载文件到本地请参考前一篇Python实现FTP下载远端文件和目录

一 . 几个模块

from ftplib import FTP
ftp = FTP() #设置变量
ftp.set_debuglevel(2) #打开调试级别2 显示详细信息
ftp.connect("IP", "port") #连接ftp, IP和端口
ftp.log("user", "password") #连接的用户名、密码
 
ftp.cwd(pathname) #设置FTP当前操作的路径
ftp.dir() #显示目录下的文件信息
ftp.nlst() #获取目录下的文件
ftp.mkd(pathname) #新建远程目录
ftp.pwd() #返回当前所在位置
ftp.rmd(dirname) #删除远程目录
ftp.delete(filename) #删除远程文件
ftp.rename(from_name, to_name) #修改文件名
ftp.storbinaly("STOR filename.txt", file_handel, bufsize) # 上传目标文件
ftp.retrbinaly("RETR filename.txt", file_handel, bufsize) # 上传FTP文件
ftp.size(filename) # 返回服务器中的文件大小


二. 功能实现:

 1. 上传整个目录包括子目录

def upload_file_tree(local_path, remote_path, ftp, IsRecursively):

核心函数

入参:local_path:本地目录,可以是当前路径

          remote_path: 远端目录 ,必须是服务器上存在的且有权限访问的目录

          ftp: FTP() 创建的类对象

          IsRecursively:是否递归即是否下载所有文件包含子目录 True或者False

2. 指定文件名上传

def upload_file(local_file, remote_file, ftp):

核心函数

入参:local_file本地目录,可以是当前路径

          remote_file: 远端目录 ,必须是服务器上存在的且有权限访问的目录

          ftp: FTP() 创建的类对象

3. 简单的检查函数

def is_same_size(ftp, local_file, remote_file):

入参:local_file:下载完成后本地存在此文件

          remote_file: 远端文件名, 传入远端文件名即可

          ftp:FTP() 创建的类对象

注意ftp切换目录时,递归上传目录后需要返回上一层

代码如下,只需修改if __name__ == '__main__':   内的目录和文件名即可 是否递归上传源目录下的子目录通过IsRecursively 控制

#! /usr/bin/python # -*- coding: utf-8 -*
#import unittest # 单元测试用例
import os
import re
import sys
import datetime,time
from ftplib import FTP # 定义了FTP类,实现ftp上传和下载
import traceback
import logging

FTP_PERFECT_BUFF_SIZE = 8192

'''
@检查是否下载/上传完整
'''
def is_same_size(ftp, local_file, remote_file):
    """判断远程文件和本地文件大小是否一致
 
       参数:
         local_file: 本地文件

         remote_file: 远程文件
    """
    try:
        remote_file_size = ftp.size(remote_file)
    except Exception as err:
        logging.debug("get remote file_size failed, Err:%s" % err)
        remote_file_size = -1

    try:
        local_file_size = os.path.getsize(local_file)
    except Exception as err:
        logging.debug("get local file_size failed, Err:%s" % err)
        local_file_size = -1

    #三目运算符
    result = True if(remote_file_size == local_file_size) else False

    return (result, remote_file_size, local_file_size)

'''
@实际负责上传功能的函数
'''
def upload_file(local_file, remote_file, ftp):
    # 本地是否有此文件
    if not os.path.exists(local_file):
        logging.debug('no such file or directory %s.' (local_file))
        return False

    result = False
    remote_file_size = local_file_size = -1

    result, remote_file_size, local_file_size = is_same_size(ftp, local_file, remote_file)
    if True != result:
        print('remote_file %s is not exist, now trying to upload...' %(remote_file))
        logging.debug('remote_file %s is not exist, now trying to upload...' %(remote_file))  
        global FTP_PERFECT_BUFF_SIZE
        bufsize = FTP_PERFECT_BUFF_SIZE
        try:
            with open(local_file, 'rb') as file_handler :
                #ftp.retrbinary('RETR %s' % remote_file, f.write, bufsize)
                
                if ftp.storbinary('STOR ' + remote_file, file_handler, bufsize):
                    result, remote_file_size, local_file_size = is_same_size(ftp, local_file, remote_file)
        except Exception as err:
            logging.debug('some error happend in storbinary file :%s. Err:%s' %(local_file, traceback.format_exc()))
            result = False

    logging.debug('Upload 【%s】 %s , remote_file_size = %d, local_file_size = %d.' \
    %(remote_file, 'success' if (result == True) else 'failed', remote_file_size, local_file_size))
    print('Upload 【%s】 %s , remote_file_size = %d, local_file_size = %d.' \
    %(remote_file, 'success' if (result == True) else 'failed', remote_file_size, local_file_size))

"""
上传整个目录下的文件
"""
def upload_file_tree(local_path, remote_path, ftp, IsRecursively):
    print("remote_path:", remote_path)

    #创建服务器目录 如果服务器目录不存在 就从当前目录创建目标外层目录
    # 打开该远程目录
    try:
        ftp.cwd(remote_path)  # 切换工作路径
    except Exception as e:
        print('Except INFO:', e)
        base_dir, part_path = ftp.pwd(), remote_path.split('/')
        for subpath in part_path:
            # 针对类似  '/home/billing/scripts/zhf/send' 和 'home/billing/scripts/zhf/send' 两种格式的目录
            #如果第一个分解后的元素是''这种空字符,说明根目录是从/开始,如果最后一个是''这种空字符,说明目录是以/结束
            #例如 /home/billing/scripts/zhf/send/ 分解后得到 ['', 'home', 'billing', 'scripts', 'zhf', 'send', ''] 首位和尾都不是有效名称
            if '' == subpath:
                continue
            base_dir = os.path.join(base_dir, subpath) #base_dir + subpath + '/'  # 拼接子目录
            try:
                ftp.cwd(base_dir)  # 切换到子目录, 不存在则异常
            except Exception as e:
                print('Except INFO:', e)
                print('remote not exist directory %s , create it.' %(base_dir))
                ftp.mkd(base_dir)  # 不存在创建当前子目录 直到创建所有
                continue

    #本地目录切换
    try:
        #os.chdir(local_path)
        #远端目录通过ftp对象已经切换到指定目录或创建的指定目录

        file_list = os.listdir(local_path)
        #print(file_list)
        for file_name in file_list:
            if os.path.isdir(os.path.join(local_path, file_name) ):
                print('%s is a directory...' %(file_name))
                if IsRecursively: #递归目录上传
                    #创建相关的子目录 创建不成功则目录已存在
                    try:
                        cwd = ftp.pwd()
                        ftp.cwd(file_name)  # 如果cwd成功 则表示该目录存在 退出到上一级
                        ftp.cwd(cwd)
                    except Exception as e:
                        print('check remote directory %s not eixst, now trying to create it! Except INFO:%s.' %(file_name, e))
                        ftp.mkd(file_name)
                    
                    print('trying to upload directory %s --> %s ...' %(file_name, remote_path))
                    logging.debug('trying to upload directory %s --> %s ...' %(file_name, remote_path))
                    p_local_path = os.path.join(local_path, file_name)
                    p_remote_path = os.path.join(ftp.pwd(), file_name)
                    upload_file_tree(p_local_path, p_remote_path, ftp, IsRecursively)
                    #对于递归 ftp 每次传输完成后需要切换目录到上一级
                    ftp.cwd("..")
                else:
                    logging.debug('translate mode is UnRecursively, %s is a directory, continue ...' %(file_name))
                    continue
            else:
                #是文件 直接上传   
                local_file = os.path.join(local_path, file_name) 
                remote_file = os.path.join(remote_path, file_name)
                if upload_file(local_file, remote_file, ftp):
                    pass
                    #logging.debug('upload %s success, delete it ...' %(local_file))
                    #os.unlink(local_file)
    except:
        logging.debug('some error happend in upload file :%s. Err:%s' %(file_name, traceback.format_exc()))
    return


if __name__ == '__main__':  
    """  定义LOG名 """
    current_time = time.time()
    str_time = time.strftime('%Y%m%d%H%M%S',time.localtime(current_time ))
    log_file_name = 'ftpput' + str_time  + '.log'

    LOG_FORMAT = "%(message)s" #"%(asctime)s %(name)s %(levelname)s %(pathname)s %(message)s "#配置输出日志格式
    DATE_FORMAT = '%Y-%m-%d  %H:%M:%S %a ' #配置输出时间的格式,注意月份和天数不要搞乱了
    LOG_PATH = os.path.join(os.getcwd(), log_file_name)
    logging.basicConfig(level=logging.DEBUG,
                        format=LOG_FORMAT,
                        datefmt = DATE_FORMAT ,
                        filemode='w', #覆盖之前的记录 'a'是追加
                        filename=LOG_PATH #有了filename参数就不会直接输出显示到控制台,而是直接写入文件
                        )


    host = '1.2.3.4'
    port = 21
    username = 'li'
    password = 'ly'
    ftp = FTP()
    ftp.connect(host,port)
    ftp.login(username, password)

    ''' 上传整个路径下的文件 '''
    local_path = 'li'
    remote_path = 'ly'
    #local_file =None
    #remote_file = None
    IsRecursively = True
    #递归传输本地所有文件包括目录 递归开关 IsRecursively
    upload_file_tree(local_path, remote_path, ftp, IsRecursively)
    #传递本地指定文件
    #upload_file(local_file, remote_file, ftp)

上传的过程: 初次获取远端文件大小会失败,抛出错:get remote file_size failed, Err:550 Could not get file size.,但是不影响程序,继续走后面的程序分支上传。

  • 4
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值