python搭建FTP

1、FTP服务的主动模式和被动模式:

主动模式工作过程:
  1. 客户端以随机非特权端口N,就是大于1024的端口,对server端21端口发起连接
  2. 客户端开始监听 N+1端口;
  3. 服务端会主动以20端口连接到客户端的N+1端口。
主动模式的优点:

服务端配置简单,利于服务器安全管理,服务器只需要开放21端口

主动模式的缺点:

如果客户端开启了防火墙,或客户端处于内网(NAT网关之后), 那么服务器对客户端端口发起的连接可能会失败
被动模式:

被动模式工作过程:
  1. 客户端以随机非特权端口连接服务端的21端口
  2. 服务端开启一个非特权端口为被动端口,并返回给客户端
  3. 客户端以非特权端口+1的端口主动连接服务端的被动端口
被动模式缺点:

服务器配置管理稍显复杂,不利于安全,服务器需要开放随机高位端口以便客户端可以连接,因此大多数FTP服务软件都可以手动配置被动端口的范围

被动模式的优点:

对客户端网络环境没有要求

2、搭建客户端通过【主动模式】连接服务器

服务器端配置(以下配置默认为有公网ip的服务器):
①安全组入方向映射:TCP/21端口
开别的端口会出现:ftplib.error_perm: 501 Rejected data connection to foreign address xxx.
②安全组出方向打开允许TCP所有端口
③pip3 install pyftpdlib

主动模式下,服务器端代码
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer


# filepath = r"F:\videos"
filepath = "/root/workspace/zys/ftpfiles"

# 新建一个用户组
authorizer = DummyAuthorizer()
# 将用户名,密码,指定目录,权限 添加到里面
authorizer.add_user("xxx", "xxx", filepath, perm="elradfmwM")
# 这个是添加匿名用户,任何人都可以访问,如果去掉的话,需要输入用户名和密码,可以自己尝试
# authorizer.add_anonymous(filepath)

handler = FTPHandler
handler.authorizer = authorizer

# 开启服务器
port = 21
server = FTPServer(("0.0.0.0", port), handler)
server.serve_forever()


# 读取权限
# "e" ——更改目录(CWD,CDUP命令)
# "l" ——列表文件(LIST,NLST,STAT,MLSD,MLST,SIZE命令)
# "r" ——从服务器检索文件(RETR命令)
# 写入权限
# "a" ——将数据追加到现有文件(APPE命令)
# "d" ——删除文件或目录(DELE,RMD命令)
# "f" ——重命名文件或目录(RNFR,RNTO命令)
# "m" ——创建目录(MKD命令)
# "w" ——将文件存储到服务器(STOR,STOU命令)
# "M"——更改文件模式/权限(SITE CHMOD命令)
# "T"——更改文件修改时间(SITE MFMT命令)

主动模式下,客户端代码(被动模式加了try-catch,可以参考)
from ftplib import FTP
import datetime
def ftpconnect(host, port, username, password):
    ftp = FTP()
    ftp.set_debuglevel(2)
    ftp.connect(host, port)
    ftp.login(username, password)
    ftp.set_pasv(False) ########################主动模式
    print(ftp.getwelcome())
    return ftp

def downloadfile(ftp, remotepath, localpath):
    # 从ftp下载文件
    bufsize = 1024
    fp = open(localpath, 'wb')
    ftp.retrbinary('RETR ' + remotepath, fp.write, bufsize)
    ftp.set_debuglevel(0)
    fp.close()


def uploadfile(ftp, localpath, remotepath):
    # 从本地上传文件到ftp
    bufsize = 1024
    fp = open(localpath, 'rb')
    ftp.storbinary('STOR ' + remotepath, fp, bufsize)
    ftp.set_debuglevel(0)
    fp.close()

if __name__ == "__main__":
    ip = "xxxxxxx"
    ftp = ftpconnect(ip, 21, "xxx", "xxx")

    local_file = r'F:\videos\2023-02-04_01-58-09.mp4'
    for i in range(2):
        target_file = str(i) + 'xx.mp4'
        uploadfile(ftp, local_file, target_file)
    # downloadfile(ftp, "VID_20210830_103249.mp4", "11.mp4")
    ftp.quit()

3、搭建客户端通过【被动模式】连接服务器

服务器端配置(以下配置默认为有公网ip的服务器):
①安全组-入方向映射:TCP/21端口,和passive_ports设定的主动模式下端口范围
②安全组出方向不用设置
③pip3 install pyftpdlib

被动模式下,服务器端代码
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer


# filepath = r"F:\videos"
filepath = "/root/workspace/zys/ftpfiles"


# 新建一个用户组
authorizer = DummyAuthorizer()
# 将用户名,密码,指定目录,权限 添加到里面
authorizer.add_user("xxx", "xxx", filepath, perm="elradfmwM")
# 这个是添加匿名用户,任何人都可以访问,如果去掉的话,需要输入用户名和密码,可以自己尝试
# authorizer.add_anonymous(filepath)

handler = FTPHandler
handler.authorizer = authorizer
# 

#*******************添加被动端口范围, client主动模式下,不需要设置********************************
handler.passive_ports = range(38300, 38301)
#***********************************************************************

# 开启服务器
port = 21
server = FTPServer(("0.0.0.0", port), handler)
server.serve_forever()


# 读取权限
# "e" ——更改目录(CWD,CDUP命令)
# "l" ——列表文件(LIST,NLST,STAT,MLSD,MLST,SIZE命令)
# "r" ——从服务器检索文件(RETR命令)
# 写入权限
# "a" ——将数据追加到现有文件(APPE命令)
# "d" ——删除文件或目录(DELE,RMD命令)
# "f" ——重命名文件或目录(RNFR,RNTO命令)
# "m" ——创建目录(MKD命令)
# "w" ——将文件存储到服务器(STOR,STOU命令)
# "M"——更改文件模式/权限(SITE CHMOD命令)
# "T"——更改文件修改时间(SITE MFMT命令)

被动模式下,客户端代码(重写makepasv方法)
from ftplib import FTP
import datetime


# 重写makepasv方法, *************非常重要***************
# 作用是为了数据传输时使用服务器被动模式下返回的ip和port
# 防止出现卡在:*resp* '227 Entering passive mode (192,168,1,1x,xx,xxx).'
class MyFtp(FTP):
    def makepasv(self):
        host, port = super(MyFtp, self).makepasv()
        host = self.sock.getpeername()[0]
        print(host, port)
        return host, port


def ftpconnect(host, port, username, password):
    connectStatus = True
    ftp = MyFtp()
    # ftp.set_debuglevel(2)
    try:
        ftp.connect(host, port)
        ftp.login(username, password)
        print("*********************************************************", ftp.getwelcome())
    except ConnectionRefusedError as e:
        print("ftp服务连接失败!")
        connectStatus = False
    return connectStatus, ftp


def downloadfile(ip, port, remotepath, localpath):
    # ftp连接
    connectStatus, ftp = ftpconnect(ip, port, "xxx", "xxx")
    if not connectStatus:
        pass
    try:
        # 从ftp下载文件
        bufsize = 1024
        fp = open(localpath, 'wb')
        ftp.retrbinary('RETR ' + remotepath, fp.write, bufsize)
        ftp.set_debuglevel(0)
        fp.close()
    except EOFError as e:
        print("FTP服务异常,传输失败!")
    except ConnectionAbortedError as e:
        print("FTP服务异常,传输失败!")
    except TimeoutError as e:
        print("FTP服务异常,传输失败!")
    except Exception as e:
        print("FTP服务异常,传输失败!")

def uploadfile(ip, port, localpath, remotepath):
    # ftp连接
    connectStatus, ftp = ftpconnect(ip, port, "xxx", "xxx")
    if not connectStatus:
        pass
    try:
        # 从本地上传文件到ftp
        bufsize = 1024
        fp = open(localpath, 'rb')
        ftp.storbinary('STOR ' + remotepath, fp, bufsize)
        ftp.set_debuglevel(0)
        fp.close()
        ftp.quit()
    except EOFError as e:
        print("FTP服务异常,传输失败!")
    except ConnectionAbortedError as e:
        print("FTP服务异常,传输失败!")
    except TimeoutError as e:
        print("FTP服务异常,传输失败!")
    except Exception as e:
        print("FTP服务异常,传输失败!")


import time
if __name__ == "__main__":

    ip = "xxxxx"
    port = xxx # 可以是云服务的21端口,也可以是服务器21端口映射到外网的端口

    local_file = r'F:\videos\2023-02-04_01-58-09.mp4'
    for i in range(5):
        target_file = str(i) + 'a.mp4'
        try:
            uploadfile(ip, port, local_file, target_file)
        except Exception as e:
            print(e)
        time.sleep(5)
        # downloadfile(ftp, "VID_20210830_103249.mp4", "11.mp4")


本次过程中出现的错误:
①主动模式:ftplib.error_perm: 501 Rejected data connection to foreign address 192.168.1.xxx.11.
②被动模式,卡在resp '227 Entering passive mode (192,168,1,1x,xx,xxx).'

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容