Python搭建FTP服务器与客户端

来自CloudWiki
跳转至: 导航搜索

FTP工作原理

熟悉FTP的读者可能会觉得这个太简单了,直接在网上下载软件安装运行就可以了,客户端和服务器都有,但是只能满足一些简单的工作需求。如果我们通过写Python代码搭建FTP服务器和客户端,就能实现一些更为精细化的控制,如精细的访问权限配置、详细的日志记录等,根据工作经验,Python搭建FTP服务器也非常简单,而且更为稳定,下面就让我们一起来学习吧。

简单总结:主动方式对FTP服务器的管理有利,但对客户端的管理不利。因为FTP服务器企图与客户端的高位随机端口建立连接,而这个端口很有可能被客户端的防火墙阻塞掉。被动方式对FTP客户端的管理有利,但对服务器端的管理不利。因为客户端要与服务器端建立两个连接,其中一个连到一个高位随机端口,而这个端口很有可能被服务器端的防火墙阻塞掉。

FTP的搭建

安装模块:

pip3 install pyftpdlib

如果Python3.7.0以上版本安装时报错ModuleNotFoundError: No module named '_ctypes',

原因在这里:https://www.jianshu.com/p/69681655309b


可以 执行命令:yum install libffi-devel -y

然后参照Centos7 安装python3将Python重新安装一遍即可。

服务器端

方法1:快速搭建一个简单的FTP服务器

 python3 -m pyftpdlib -p 21

即可在执行命令所在的目录下建立一个端口为21的供下载文件的FTP服务器,

注意Linux系统需要root用户才能使用默认端口21,

建立之后我们可以远程利于windows文件管理器打开:

Python20-12-1.png


windows系统中目录文件名可能是乱码,原因是pyftpdlib内部使用utf8,而windows使用gbk,参照下面的步骤可解决windows系统的乱码问题:

windows系统解决乱码的办法:

首先,找到pyftpdlib源文件所在的目录。

>>> import pyftpdlib
>>> pyftpdlib.__path__
['C:\\Users\\xx\\projectA_env\\lib\\site-packages\\pyftpdlib']

其次,在目录pyftpdlib源文件所在的目录找到文件filesystems.py和handlers.py,先备份。

再次,打开filesystems.py,找到

yield line.encode('utf8', self.cmd_channel.unicode_errors)

共有两处,修改'utf8'为'gbk',保存并退出。

打开handlers.py,找到

return bytes.decode('utf8', self.unicode_errors)

修改utf8为gbk,保存并退出。

最后,验证乱码已解决。

方法二:搭建一个具有访问权限,可配置相关信息的FTP服务器(ftpserver.py)

python3 ftpserver.py

[I 2020-01-18 21:50:18] >>> starting FTP server on 0.0.0.0:21, pid=65456 <<<
[I 2020-01-18 21:50:18] concurrency model: async
[I 2020-01-18 21:50:18] masquerade (NAT) address: None
[I 2020-01-18 21:50:18] passive ports: 2000->2332

ftpserver.py:

(以下为windows下的的代码,linux的只需把路径'd:/' 改成 ‘.’即可)

from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler,ThrottledDTPHandler
from pyftpdlib.servers import FTPServer
from pyftpdlib.log import LogFormatter
import logging


#记录日志,默认情况下日志仅输出到屏幕(终端),这里即输出到屏幕又输出到文件,方便日志查看
logger = logging.getLogger()
logger.setLevel(logging.INFO)
ch = logging.StreamHandler()
fh = logging.FileHandler(filename='myftpserver.log',encoding='utf-8') #默认的方式是追加到文件
ch.setFormatter(LogFormatter())
fh.setFormatter(LogFormatter())
logger.addHandler(ch) #将日志输出至屏幕
logger.addHandler(fh) #将日志输出至文件


# 实例化虚拟用户,这是FTP验证首要条件
authorizer = DummyAuthorizer()
# 添加用户权限和路径,括号内的参数是(用户名, 密码, 用户目录, 权限),可以为不同的用户添加不同的目录和权限
authorizer.add_user("user", "12345", "d:/", perm="elradfmw")
# 添加匿名用户 只需要路径
authorizer.add_anonymous("d:/")

# 初始化ftp句柄
handler = FTPHandler
handler.authorizer = authorizer

#添加被动端口范围
handler.passive_ports = range(2000, 2333)

# 下载上传速度设置
dtp_handler = ThrottledDTPHandler
dtp_handler.read_limit = 300 * 1024 #300kb/s
dtp_handler.write_limit = 300 * 1024 #300kb/s
handler.dtp_handler = dtp_handler

# 监听ip 和 端口,linux里需要root用户才能使用21端口
server = FTPServer(("0.0.0.0", 21), handler)

# 最大连接数
server.max_cons = 150
server.max_cons_per_ip = 15

# 开始服务,自带日志打印信息
server.serve_forever()

登录ftp并进行测试:

C:\Users\thinkpad>ftp 192.168.1.102
连接到 192.168.1.102。
220 pyftpdlib 1.5.5 ready.
530 Log in with USER and PASS first.
用户(192.168.1.102:(none)): user
331 Username ok, send password.
密码:
230 Login successful.
ftp> dir
200 Active data connection established.
125 Data connection already open. Transfer starting.
drwxrwxrwx   1 owner    group           0 Oct 02 06:41 $RECYCLE.BIN
drwxrwxrwx   1 owner    group           0 Oct 19 00:09 .android
drwxrwxrwx   1 owner    group        4096 Nov 28  2018 360Rec
drwxrwxrwx   1 owner    group           0 Dec 21 06:43 BaiduYunDownload
drwxrwxrwx   1 owner    group        4096 Jun 16  2017 Biz
drwxrwxrwx   1 owner    group           0 Oct 21 06:48 Config.Msi
dr-xr-xr-x   1 owner    group        4096 Jan 13 01:09 Game
drwxrwxrwx   1 owner    group        4096 Dec 02 14:05 Life
drwxrwxrwx   1 owner    group        8192 Jul 01  2019 MyStore
dr-xr-xr-x   1 owner    group        4096 Nov 25 02:57 OneDrive
drwxrwxrwx   1 owner    group       49152 Jan 18 06:27 Program Files
dr-xr-xr-x   1 owner    group        4096 Jan 13 01:09 Project
drwxrwxrwx   1 owner    group           0 Jan 13  2018 PycharmProjects
drwxrwxrwx   1 owner    group           0 Dec 10 02:33 QLDownload
drwxrwxrwx   1 owner    group           0 Jun 26  2016 System Volume Information
dr-xr-xr-x   1 owner    group        4096 Jan 13 01:09 Tech
drwxrwxrwx   1 owner    group           0 Oct 04 09:25 VS09_PROJECTS
drwxrwxrwx   1 owner    group           0 Oct 04 09:25 VS10_PROJECTS
drwxrwxrwx   1 owner    group           0 Nov 17 00:15 WUDownloadCache
drwxrwxrwx   1 owner    group        8192 Jan 15 12:49 Work
drwxrwxrwx   1 owner    group           0 Dec 15 00:13 d94083babee197af8beff6f0c833622e
dr-xr-xr-x   1 owner    group        4096 Jan 17 08:16 teaching
drwxrwxrwx   1 owner    group        4096 Jan 17 01:29 tttt
226 Transfer complete.

客户端

# -*- encoding:utf-8 -*-
from ftplib import FTP
#登陆FTP
ftp = FTP(host='10.0.0.30',user='user',passwd='12345')
#设置编码方式,由于在windows系统,设置编码为gbk
ftp.encoding = 'gbk'
# 切换目录
ftp.cwd('test')
#列出文件夹的内容
ftp.retrlines('LIST') # ftp.dir()
#下载文件 note.txt
ftp.retrbinary('RETR note.txt', open('note.txt', 'wb').write)
#上传文件 ftpserver.py
ftp.storbinary('STOR sample.txt', open('sample.txt', 'rb'))
#查看目录下的文件详情
for f in ftp.mlsd(path='/test'):
    print(f)