python 基于selectors库实现文件上传与下载

 更新时间:2020年12月31日 11:34:49   作者:槑槑的离生  
这篇文章主要介绍了python 基于selectors库实现文件上传与下载的示例代码,帮助大家更好的理解和使用python,感兴趣的朋友可以了解下

server.py

import selectors
import socket
import os
import time


BASE_DIR =os.path.abspath(os.path.dirname(__file__))

class selectFtpserver:
  def __init__(self):
    self.dic = {} # 创建空字典
    self.hasReceived = 0
    self.hasSend=0
    self.sel = selectors.DefaultSelector() # 生成一个select对象
    self.create_socket() #create_socket()是创建socket对象函数完成绑定功能
    self.hanle() #handle()函数完成循环监听

  def create_socket(self):
    sock = socket.socket()
    sock.bind(('127.0.0.1', 8899))
    sock.listen()
    sock.setblocking(False)
    self.sel.register(sock, selectors.EVENT_READ, self.accept) # 把刚生成的sock连接对象注册到select连接列表中,并交给accept函数处理
    print("服务端已打开,请连接客户端")

  def hanle(self):
    while True:
      events = self.sel.select() # 默认是阻塞,有活动连接就返回活动的连接列表
      # 这里看起来是select,其实有可能会使用epoll,如果你的系统支持epoll,那么默认就是epoll
      # print("event==",events)
      for key, mask in events:
        callback = key.data # 去调accept函数
        callback(key.fileobj, mask) # key.fileobj就是readable中的一个socket连接对象

  def accept(self,sock, mask):
    conn, addr = sock.accept() # Should be ready
    print('accepted', conn, 'from', addr)
    conn.setblocking(False) # 设定非阻塞
    self.sel.register(conn, selectors.EVENT_READ, self.read) # 新连接注册read回调函数
    self.dic[conn] = {} # 在空字典里进行了conn赋值,self.dic={conn:{},}

  def read(self, conn, mask): # 接收了conn和mask
    try: # 加异常防止客户端突然断开
      if not self.dic[conn]: # 判断self.dic[conn]里面是否是空字典,如果是空字典,代表第一次进来
        print('====第一次进来')
        data = conn.recv(1024) # conn接收了客户端发来的数据
        print("data==",str(data, encoding='utf-8'))
        cmd, filename,filesize = str(data, encoding='utf-8').split('|') # 把接收到客户端发来的包解开拿到cmd,filename,filesize个信息
        self.dic = {conn: {"cmd": cmd, "filename": filename, "filesize": int(filesize)}} # 把拿到的cmd,filename,filesize信息放到self.dic字典里去后程序返回到handle()函数里的events继续监听
        print(self.dic)
        if cmd == 'put': # 如果接收的信息是put
          conn.send(bytes("OK", encoding='utf8')) # 给客户端返回一条数据
        if self.dic[conn]['cmd'] == 'get':
          file = os.path.join(BASE_DIR, "upload", filename)

          if os.path.exists(file):
            print("文件存在的情况,返回YES给客户端")
            filesize = os.path.getsize(file)
            self.dic[conn]['filesize'] = filesize
            print("self.dic",self.dic)
            send_info = '%s|%s' % ('YES', filesize)
            conn.send(bytes(send_info, encoding='utf8'))
          else:
            print("文件不存在情况下")
            send_info = '%s|%s' % ('NO', 0)
            conn.send(bytes(send_info, encoding='utf8'))
            self.dic[conn] = {} #文件不存在的情况下,要将清空字典
      else: # 如果不是空字典代表不是第一次进来
        print('不是第一次来的')
        print(self.dic)
        if self.dic[conn].get('cmd', None): # 对接收的命令进行分发判断是put还是get
          cmd = self.dic[conn].get('cmd')
          if hasattr(self, cmd): # 如果cmd=put调用put函数,如果是cmd=get函数调用get函数
            func = getattr(self, cmd)
            func(conn)
          else:
            print("error cmd!")
            conn.close()
        else:
          print("error cmd!")
          conn.close()
    except Exception as e:
      print('断开的客户端信息是:', conn)
      self.sel.unregister(conn) # 如果没有接收到数据做一个关闭解除
      conn.close()

    # put上传函数
  def put(self, conn):
    fileName = self.dic[conn]['filename']
    fileSize = self.dic[conn]['filesize']
    # print("BASE_DIR",BASE_DIR)
    path = os.path.join(BASE_DIR, "upload", fileName) # 拿到要接收的信息
    # print(fileName,fileSize,path)

    recv_data = conn.recv(1024) # 接收客户端上传的数据1024字节
    self.hasReceived += len(recv_data) # 把接收的数据累加到变量self.hasReceived

    with open(path, 'ab') as f: # 打开文件
      f.write(recv_data) # 把接收的数据写到文件里去

    if fileSize == self.hasReceived: # 判断文件大小跟接收大小是否一样
      if conn in self.dic.keys(): # 如果文件大小跟接收大小一样清空字典
        self.dic[conn] = {}
      self.hasReceived = 0 #S上传结束之后,需要将self.hasReceived 重置成功
      print("%s 上传完毕!" % fileName)

  def get(self,conn):
    fileName = self.dic[conn]['filename']
    file = os.path.join(BASE_DIR, "upload", fileName)
    # fileSize = os.path.getsize(file)
    fileSize=self.dic[conn]['filesize']

    data = conn.recv(1024) # conn接收了客户端发来的数据
    dataOK = str(data, encoding='utf-8')

    if dataOK == 'OK':
      with open(file, 'rb') as f: # 打开文件
        while fileSize > self.hasSend: # 循环的发送文件给客户端
          contant = f.read(1024)
          recv_size = len(contant)
          conn.send(contant)
          self.hasSend += recv_size
          s = str(int(self.hasSend / fileSize * 100)) + "%"
          print("正在下载文件: " + fileName + " 已经下载:" + s)

      if fileSize == self.hasSend: # 判断文件大小跟接收大小是否一样
        if conn in self.dic.keys(): # 如果文件大小跟接收大小一样清空字典
          self.dic[conn] = {}
        print("%s 下载完毕!" % fileName)
        self.hasSend = 0

if __name__ == '__main__':
  selectFtpserver()

client.py

import socket
import os,sys
BASE_DIR=os.path.dirname(os.path.abspath(__file__))

class selectFtpClient:
  def __init__(self):
    self.args=sys.argv               #sys.argv在命令行输入的参数,第一个参数默认文件名,第二个参数跟IP地址和端口
    if len(self.args)>1:              #如果大于1把第二个参数俩个值赋值给port
      self.port=(self.args[1],int(self.args[2]))
    else:
      self.port=("127.0.0.1",8899)        #如果没有第二个参数默认取这个
    self.create_socket()               #
    self.command_fanout()              #进行命令分发
    self.mainPath = os.path.join(BASE_DIR, 'filename') # 获取该客户端下的filename路径

  #create_socket函数创建socket对象连接服务端
  def create_socket(self):
    try:
      self.sk = socket.socket()
      self.sk.connect(self.port)
      print('连接FTP服务器成功!')
    except Exception as e:
      print("eroor:",e)

  #command_fanout()函数进行命令分发
  def command_fanout(self):
    while True:
      try:
        print("----------------welcome to ftp client-------------------")
        self.help_info()
        cmd_info = input('>>>请输入操作命令:').strip() # put 12.png images
        if not cmd_info:
          continue
        cmd,file = cmd_info.split() ##按照空格分隔
        # print("命令是什么", cmds)
        if cmd == "quit":
          break
        if hasattr(self, cmd):
          func = getattr(self, cmd)
          func(cmd,file)
          Tag = input("是否继续进入ftp clinet,请选择Y/N:").strip()
          if Tag.upper() == 'Y':
            continue
          else:
            break
        else:
          print('No such command ,please try again')
      except Exception as e: # server关闭了
        print('%s' % e)
        break

  def help_info(self):
    print ('''
       get + (文件名)  表示下载文件
       put + (文件名)  表示上传文件
       quit       表示退出登录
    ''')

  #put()上传函数
  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) #给文件名字大小打包成fileInf
      self.sk.send(bytes(fileInfo, encoding='utf8')) #调用send方法把fileInf发给服务端
      recvStatus = self.sk.recv(1024)         #接收服务端返回的OK内容
      print('recvStatus' , recvStatus)
      hasSend = 0
      if str(recvStatus, encoding='utf8') == "OK":  #如果接收到服务端返回的OK
        with open(file, 'rb') as f:        #打开文件
          while fileSize > hasSend :       #循环的去上传文件
            contant = f.read(1024)
            recv_size = len(contant)
            self.sk.send(contant)
            hasSend += recv_size
            s=str(int(hasSend/fileSize*100))+"%"
            print("正在上传文件: "+fileName+" 已经上传:" +s)
        print('%s文件上传完毕' % (fileName,))
    else:
      print('要上传的文件不存在')

  #get()下载函数
  def get(self,cmd,fileName):
    path = os.path.join(BASE_DIR, "download", fileName) # 拿到要接收的信息
    fileSize=0
    fileInfo = '%s|%s|%s' % (cmd, fileName, fileSize) # 给文件名字大小打包成fileInf
    print(fileInfo)
    self.sk.send(bytes(fileInfo, encoding='utf8')) # 调用send方法把fileInfo发给服务端

    recvdata = self.sk.recv(1024) # 接收服务端返回的是否存在文件内容
    recvStatus, fileSize = str(recvdata, encoding='utf-8').split('|')
    print("recvStatus==",recvStatus,fileSize)
    fileSize = int(fileSize)

    hasReceived = 0
    if recvStatus == "YES": # 如果接收到服务端返回的YES
      self.sk.send(bytes('OK', encoding='utf8')) # 通知服务端可以正常下载了

      while fileSize > hasReceived: # 循环的发送文件给客户端
        recv_data = self.sk.recv(1024) # 接收客户端上传的数据1024字节
        hasReceived += len(recv_data) # 把接收的数据累加到变量self.hasReceived
        print("hasReceived",hasReceived)

        with open(path, 'ab') as f: # 打开文件
          f.write(recv_data) # 把接收的数据写到文件里去

        if fileSize == hasReceived: # 判断文件大小跟接收大小是否一样
          print("%s 下载完毕!" % fileName)
          recvStatus = 'YESS'
    else:
       print('要下载的文件不存在')


if __name__=='__main__':
  selectFtpClient()

以上就是python 基于selectors库实现文件上传与下载的详细内容,更多关于python 上传下载的资料请关注脚本之家其它相关文章!

相关文章

  • Python异常学习笔记

    Python异常学习笔记

    这篇文章主要介绍了Python异常学习笔记,本文着重讲解了如何自定义一个异常,需要的朋友可以参考下
    2015-02-02
  • 一行代码实现Python动态加载依赖

    一行代码实现Python动态加载依赖

    本文主要介绍了Python动态加载依赖,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12
  • Python如何使用标准库tmpfile库创建临时文件

    Python如何使用标准库tmpfile库创建临时文件

    这篇文章主要介绍了Python如何使用标准库tmpfile库创建临时文件问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-02-02
  • Python如何使用内置库matplotlib绘制折线图

    Python如何使用内置库matplotlib绘制折线图

    这篇文章主要介绍了Python如何使用内置库matplotlib绘制折线图,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • Python 高级库15 个让新手爱不释手(推荐)

    Python 高级库15 个让新手爱不释手(推荐)

    对于初学者来说,这是一种简单易学的编程语言;另一个原因:大量开箱即用的第三方库,正是 23 万个由用户提供的软件包使得 Python 真正强大和流行,本文给大家分享15 个让新手爱不释手Python 高级库的相关知识,感兴趣的朋友一起看看吧
    2021-05-05
  • 安装Anaconda3及使用Jupyter的方法

    安装Anaconda3及使用Jupyter的方法

    这篇文章主要介绍了安装Anaconda3及使用Jupyter的方法,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-10-10
  • 教你使用Python从文件中提取IP地址

    教你使用Python从文件中提取IP地址

    Python提供了高效的高级数据结构,还能简单有效地面向对象编程,下面这篇文章主要给大家介绍了关于如何使用Python从文件中提取IP地址的相关资料,需要的朋友可以参考下
    2022-07-07
  • python中用cantools和can工具包解析blf文件的方法

    python中用cantools和can工具包解析blf文件的方法

    这篇文章主要给大家介绍了关于python中用cantools和can工具包解析blf文件的相关资料,blf数据不像mf4那样自带信号数据库,因为它是记日志用的,一般情况下要尽可能的小,需要的朋友可以参考下
    2023-09-09
  • python验证多组数据之间有无显著差异

    python验证多组数据之间有无显著差异

    这篇文章主要介绍了python验证多组数据之间有无显著差异,利用方差分析和卡方分布验证多组数据之间的某些属性有无显著性差异,对于连续性属性可以用方差分析,对于离散型属性可以用卡方检验。下面文章详细内容需要的小伙伴可以参考一下
    2022-01-01
  • Python利用递归和walk()遍历目录文件的方法示例

    Python利用递归和walk()遍历目录文件的方法示例

    在日常开发中经常需要检查一个“目录或文件夹”内部有没有我们想要的文件或者文件夹,下面这篇文章主要给大家介绍了关于Python利用递归和walk()遍历目录文件的相关资料,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-07-07

最新评论