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).'

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

推荐阅读更多精彩内容