Python(通过selectors模块实现FTP上传下载)

该博客展示了如何使用Python实现一个基于selectors模块的FTP服务器和客户端。服务器监听8090端口,支持文件上传(put)和下载(get)操作,客户端通过命令行交互发送上传和下载请求。在文件传输过程中,服务器会实时反馈进度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

###服务端
import os
import socket
import time
import selectors
print(os.path.abspath(__file__))#打印当前文件路径
print(os.path.dirname(os.path.abspath(__file__)))#获取当前文件路径的上一级目录路径
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
class selectFtpServer:
    def __init__(self):
        self.dic = {}
        self.hasReceived = 0
        self.sel = selectors.DefaultSelector()#给一个默认的IO多路复用方式
        self.create_socket()
        self.handler()
    def create_socket(self):
        sock = socket.socket()
        sock.bind(('127.0.0.1',8090))
        sock.listen(5)
        self.sel.register(sock,selectors.EVENT_READ,self.accept)#把sock和accept绑定
        print('服务端已开启,等待用户连接。。。')
    def handler(self):
        while True:
            event = self.sel.select()#这里面是个列表,存放点sock,conn等信息
            for key,mast in event:
                call_back = key.data#这是一个函数,accept函数,后面的accept函数和read函数是固定用法,不写这个地儿是过不去的
                call_back(key.fileobj,mast)#key.fileobj在没有链接进来时是sock,再有链接进来时存的是conn
    def accept(self,sock,mast):
        conn,addr = sock.accept()
        self.sel.register(conn,selectors.EVENT_READ,self.read)#把conn和read绑定
        self.dic[conn] = {}
    def read(self,conn,mask):
        try:
            if not self.dic[conn]:#如果列表是空的那么将执行下面的操作
                data = conn.recv(1024)
                cmd,filename,filesize = str(data,encoding='utf-8').split('|')
                self.dic[conn] = {
                    "cmd":cmd,
                    "filename":filename,
                    "filesize":filesize
                }
                print(self.dic)
                if cmd == 'put':
                    conn.send(bytes('OK',encoding='utf8'))
                if self.dic[conn]['cmd'] == 'get':
                    file = os.path.join(BASE_DIR,'download',filename)
                    if os.path.exists(file):
                        fileSize = os.path.getsize(file)
                        send_info = '%s|%s'%('YES',fileSize)
                        conn.send(bytes(send_info,encoding='utf8'))
                    else:
                        send_info = '%s|%s'%('No',0)
                        conn.send(bytes(send_info, encoding='utf8'))
            else:
                if self.dic[conn].get('cmd',None):#get('cmd',None)意思是conn这个字典里如果存在‘cmd’这个键,
                    # 那么取出来,如果不存在则返回一个None
                    print(self.dic[conn].get('cmd'))
                    cmd = self.dic[conn].get('cmd')
                    if hasattr(self,cmd):
                        func = getattr(self,cmd)
                        func(conn)
                    else:
                        print('error cmd')
                        conn.close()
                else:
                    print('error cmd')
        except Exception as e:
            print('error',e)
            self.sel.unregister(conn)
            conn.close()
    def put(self,conn):
        self.hasReceived = 0
        fileName = self.dic[conn]['filename']
        fileSize = self.dic[conn]['filesize']
        path = os.path.join(BASE_DIR,'upload',fileName)#创建文件路径
        print(path)
        while self.hasReceived < int(fileSize):
            recv_data = conn.recv(1024)
            self.hasReceived += len(recv_data)
            with open(path,'wb') as f:
                f.write(recv_data)
            if int(fileSize) == self.hasReceived:
                if conn in self.dic.keys():
                    self.dic[conn] = {}
                    print('%s上传完毕!'%fileName)
    def get(self,conn):
        filename = self.dic[conn]['filename']
        path = os.path.join(BASE_DIR,'download',filename)
        if str(conn.recv(1024),'utf-8') =='second_active':
            with open(path,'rb') as f:
                for line in f:
                    conn.send(line)
            self.dic[conn] = {}
            print('文件下载完毕')
if __name__ == '__main__':
    ftp = selectFtpServer()
###客户端
import socket
import time
import os
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
class selectFtpClient:
    def __init__(self):
        self.createSocket()
        self.command_out()
    def createSocket(self):
        try:
            self.client = socket.sockput
            self.client.connect(('127.0.0.1',8090))
            print('链接FTP服务器成功')
        except Exception as e:
            print('error:',e)
    def command_out(self):
        while True:
            cmd = input('>>>>>').strip()
            if cmd == 'exit':
                break
            cmd,file = cmd.split()
            if hasattr(self,cmd):
                func = getattr(self,cmd)
                func(cmd,file)
            else:
                print('调用错误')
    def get(self,cmd,file):
        pass
    def put(self,cmd,file):
        if os.path.isfile(file):
            fileName = os.path.basename(file)
            fileSize = os.path.getsize(file)
            fileInfo = '%s|%s|%s' % (cmd, fileName, fileSize)
            print(fileInfo)
            self.client.send(bytes(fileInfo,encoding='utf8'))
            recvStatus = self.client.recv(1024)
            print('recvStatus:',recvStatus)
            hasSend = 0
            print(file)
            if str(recvStatus,encoding='utf8')== 'OK':
                with open(file,'rb') as f:
                    while fileSize>hasSend:
                        contant = f.read(1024)
                        recv_size = len(contant)
                        self.client.send(contant)
                        hasSend += recv_size
                        s = str(int(hasSend/fileSize*100)) + '%'
                        print('正在上传文件:'+fileName+'已经上传:'+s)
                    print('%s文件上传完毕'%fileName)
            else:
                print('文件不存在')
if __name__ == '__main__':
    c = selectFtpClient()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZhaoXuWen23

你的鼓励是我的动力,持续更新中

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值