Python实现win10环境下FTP的上传与下载

  • 前言
  • 导入需要的库
  • 连接到FTP服务器
  • 判断本地文件与远程文件大小是否相等
  • 上传到FTP服务器
  • 上传单个文件
  • 上传目录下的文件(递归目录)
  • 从FTP服务器下载
  • 下载单个文件
  • 下载目录下的文件(递归目录)
  • 测试功能
  • 总结
  • 源码
  • 参考博客


前言

由于项目需要,需要在本机上模拟FTP的上传与下载,故最近着手在Win10下搭建FTP服务器,接下来就是用代码实现FTP的上传和下载功能

导入需要的库

import os	# 本地目录操作
from ftplib import FTP		# 远程FTP服务器操作
from time import strftime	# 导入这个库是为了获取当前日期,用于目录的命名

连接到FTP服务器

def ftp_connect(ip_addr, port, username, password):
    """
    参数:
        ip_addr:ip地址
        port:端口号
        username:用户名
        password:密码
    """
    ftp = FTP()
    try:
        print("正在连接 %s" % ip_addr)
        ftp.connect(ip_addr, port)  # 连接FTP服务器
        print("成功连接 %s" % ip_addr)
        print("正在登录...")
        ftp.login(username, password)  # 登录
        print("登录成功")
        return ftp
    except Exception as e:
        print("%s", e)
        return ""       # 连接失败,返回""

判断本地文件与远程文件大小是否相等

def is_same_size(ftp, local_file, remote_file):
    """
    参数:
        ftp:FTP()创建的对象
        local_file:本地文件
        remote_file:远程文件
    """
    # 获取远程文件的大小
    try:
        remote_file_size = ftp.size(remote_file)
        print("remote_file_size: " + str(remote_file_size))
    except Exception as e:
        print("%s" % e)
        remote_file_size = -1
    # 获取本地文件的大小
    try:
        local_file_size = os.path.getsize(local_file)
        print("local_file_size: " + str(local_file_size))
    except Exception as e:
        print("%s" % e)
        local_file_size = -2
    # 比较文件大小,大小相等返回True,否则返回False
    if remote_file_size == local_file_size:
        result = True
    else:
        result = False
    return result

上传到FTP服务器

上传单个文件

def upload_file(ftp, local_file, remote_file):
    """
    参数:
        ftp:FTP()创建的对象
        local_file:本地文件
        remote_file:远程文件
    """
    if not os.path.isfile(local_file):      # 判断本地文件是否存在
        print('%s 不存在' % local_file)
        return
    buf_size = 10240        # 设置缓冲区大小
    file_handler = open(local_file, 'rb')
    ftp.storbinary('STOR %s' % remote_file, file_handler, buf_size)
    file_handler.close()
    # 判断文件大小是否相等,不相等则重新上传
    if is_same_size(ftp, local_file, remote_file):
        print('上传 %s' % local_file + " 成功!")
    else:
        upload_file(ftp, local_file, remote_file)

上传目录下的文件(递归目录)

def upload_dir(ftp, local_path, remote_path):
    """
        参数:
            ftp:FTP()创建的对象
            local_path:本地目录
            remote_path:远程目录
        """
    # 打开远程目录,目录不存在则创建
    try:
        ftp.cwd(remote_path)
    except Exception:
        ftp.mkd(remote_path)
        ftp.cwd(remote_path)        # 目录创建后切换到远程目录下
    # 切换到本地目录,在FTP递归创建目录和上传文件
    try:
        os.chdir(local_path)    # 切换到本地目录
        file_list = os.listdir(os.getcwd())      # 获取文件列表
        for file_name in file_list:
            # 判断列表中各项是文件还是目录
            if not os.path.isdir(file_name):    # 若是文件,直接上传
                upload_file(ftp, file_name, file_name)
            else:   # 若是目录,在远程创建目录,递归上传
                upload_dir(ftp, file_name, file_name)
                # 上传后切换到上一级目录
                os.chdir("..")
                ftp.cwd("..")
    except Exception as e:
        print("Error: " + str(e))

从FTP服务器下载

下载单个文件

def download_file(ftp, local_file, remote_file):
    """
    参数:
        ftp:FTP()创建的对象
        local_file:本地文件
        remote_file:远程文件
    """
    # 判断远端文件是否存在
    if remote_file in ftp.nlst():
        pass
    else:
        print(remote_file + "不存在")      # 若远端文件不存在则打印错误信息
        return
    buf_size = 10240
    # 判断本地文件是否存在
    if not os.path.exists(local_file):
        with open(local_file, 'wb') as f:
            print("正在下载:" + remote_file)
            ftp.retrbinary('RETR %s' % remote_file, f.write, buf_size)
        # 判断文件大小是否相等,不相等则重新下载
        if is_same_size(ftp, local_file, remote_file):
            print("%s 下载成功" % remote_file)
        else:
        	print("大小不同,文件重新下载...")
            download_file(ftp, local_file, remote_file)
    else:
        # 如果本地文件已存在,但是不完整,则重新下载
        if not is_same_size(ftp, local_file, remote_file):
        	print("大小不同,文件重新下载...")
            with open(local_file, 'wb') as f:
                print("正在下载:" + remote_file)
                ftp.retrbinary('RETR %s' % remote_file, f.write, buf_size)
            # 判断文件大小是否相等,不相等则重新下载
            if is_same_size(ftp, local_file, remote_file):
                print("%s 下载成功" % remote_file)
            else:
                print("大小不同,文件重新下载...")
                download_file(ftp, local_file, remote_file)
        else:
            print("%s 已存在" % remote_file)

下载目录下的文件(递归目录)

def download_dir(ftp, local_dir, remote_path):
    """
    参数:
        ftp:FTP()创建的对象
        local_dir:本地路径
        remote_path:远程目录
    """
    local_dir = local_dir + '//' + remote_path
    # 如果目录不存在则创建目录
    if not os.path.exists(local_dir):
        os.makedirs(local_dir)    # 可能是多级目录,需要用到makedirs
    os.chdir(local_dir)  # 切换到下载目录
    try:
        ftp.cwd(remote_path)  # 切换到远程目录
    except Exception:
        print("%s 不存在" % remote_path)
    print("当前ftp路径:" + str(ftp.pwd()))
    print("当前下载路径:" + str(os.getcwd()))
    file_list = ftp.nlst()      # 获取下载文件列表
    print("准备下载的文件列表:" + str(file_list))
    for file_name in file_list:
        # 判断列表各项为目录或是文件
        try:        # 是目录,递归下载目录
            ftp.cwd(file_name)      # 判断是否为目录,若是目录则切换到目录下,否则出现异常
            ftp.cwd("..")       # 切换进目录后需要返回上一级
            download_dir(ftp, local_dir, file_name)        # 递归下载目录
            # 下载后切换到上一级目录
            ftp.cwd("..")
            os.chdir("..")
        except Exception:       # 是文件,直接下载
            download_file(ftp, file_name, file_name)

测试功能

# 测试上传
def ftp_upload(ftp):
    remote_dir = strftime("%Y%m%d")     # 远端目录名字以日期命名
    local_upload_dir = ".//upload//"      # 上传目录
    if not os.path.exists(local_upload_dir):        # 若上传路径不存在则提示错误信息
        print("上传失败")
        print("上传路径: " + local_upload_dir + " 不存在")
        print("请重新选择正确的上传路径")
        return
    upload_dir(ftp, local_upload_dir, remote_dir)       # 上传目录下的文件


# 测试下载
def ftp_download(ftp):
    remote_dir = strftime("%Y%m%d")  # 远端目录名字以日期命名
    local_download_dir = ".//download//"  # 下载目录
    if not os.path.exists(local_dir):      # 若下载路径不存在则创建目录
        os.makedirs(local_dir)
    os.chdir(local_dir)        # 切换到下载目录
    download_dir(ftp, str(os.getcwd()), remote_dir)       # 下载目录下的文件


if __name__ == '__main__':
    Port = 21  # 端口号默认为21
    ip = "10.20.30.40"      # 目标IP地址
    Username = "username"      # 用户名
    Password = "password"      # 密码
    FObj = ftp_connect(ip, Port, Username, Password)  # 连接FTP
    # FObj.encoding = "gbk"		# 传输路径含有中文时需要更改编码
    if FObj != "":       # 连接成功
    	# 上传和下载分开测试
        ftp_upload(FObj)
        # ftp_download(FObj)
        FObj.close()     # 结束FTP服务

总结

Python语法这块不是很难,主要用到的就只有ftplib的几个方法而已。难点在于搞懂目录结构以及目录之间的切换,搞懂如何在本地和FTP服务器上创建目录。

源码

源代码放在GitHub