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


Posted in Python onDecember 31, 2020

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 client使用http post 到server端的代码
Feb 10 Python
pyqt4教程之messagebox使用示例分享
Mar 07 Python
Python编程中NotImplementedError的使用方法
Apr 21 Python
关于Pycharm无法debug问题的总结
Jan 19 Python
ubuntu上安装python的实例方法
Sep 30 Python
Linux下升级安装python3.8并配置pip及yum的教程
Jan 02 Python
python 控制台单行刷新,多行刷新实例
Feb 19 Python
python 判断txt每行内容中是否包含子串并重新写入保存的实例
Mar 12 Python
PyChon中关于Jekins的详细安装(推荐)
Dec 28 Python
Python  Asyncio模块实现的生产消费者模型的方法
Mar 01 Python
详解python网络进程
Jun 15 Python
Python 正则模块详情
Nov 02 Python
python中Mako库实例用法
Dec 31 #Python
Python调用系统命令os.system()和os.popen()的实现
Dec 31 #Python
Python使用Opencv实现边缘检测以及轮廓检测的实现
Dec 31 #Python
python 检测nginx服务邮件报警的脚本
Dec 31 #Python
Django 实现图片上传和下载功能
Dec 31 #Python
Python wordcloud库安装方法总结
Dec 31 #Python
Python的信号库Blinker用法详解
Dec 31 #Python
You might like
PHP生成月历代码
2007/06/14 PHP
php类常量的使用详解
2013/06/08 PHP
php遍历目录输出目录及其下的所有文件示例
2014/01/27 PHP
Prototype Number对象 学习
2009/07/19 Javascript
namespace.js Javascript的命名空间库
2011/10/11 Javascript
JavaScript对象和字串之间的转换实例探讨
2013/04/21 Javascript
浅析onsubmit校验表单时利用ajax的return false无效问题
2013/07/10 Javascript
javascript date格式化示例
2013/09/25 Javascript
js导入导出excel(实例代码)
2013/11/25 Javascript
JS获得QQ号码的昵称,头像,生日的简单实例
2013/12/04 Javascript
DOM节点深度克隆函数cloneNode()用法实例
2015/01/12 Javascript
Js实现自定义右键行为
2015/03/26 Javascript
jquery合并表格中相同文本的相邻单元格
2015/07/17 Javascript
Jquery easyui开启行编辑模式增删改操作
2016/01/14 Javascript
Vue.js每天必学之计算属性computed与$watch
2016/09/05 Javascript
vue 中固定导航栏的实例代码
2019/11/01 Javascript
pyqt4教程之widget使用示例分享
2014/03/07 Python
python实现类的静态变量用法实例
2015/05/08 Python
Python多层嵌套list的递归处理方法(推荐)
2016/06/08 Python
Python解决N阶台阶走法问题的方法分析
2017/12/28 Python
Django 中使用流响应处理视频的方法
2018/07/20 Python
对python requests发送json格式数据的实例详解
2018/12/19 Python
python如何将多个PDF进行合并
2019/08/13 Python
Python中函数的返回值示例浅析
2019/08/28 Python
英国著名的药妆网站:Escentual
2016/07/29 全球购物
Myprotein瑞典官方网站:畅销欧洲英国运动营养品牌
2018/01/22 全球购物
RIP版本1跟版本2的区别
2013/12/30 面试题
信息专业个人的自我评价
2013/12/27 职场文书
缓刑人员的思想汇报
2014/01/11 职场文书
应届电子商务毕业自荐书范文
2014/02/11 职场文书
高中学生期末评语
2014/04/25 职场文书
法人委托书的范本格式
2014/09/11 职场文书
教师文明餐桌光盘行动倡议书
2015/04/28 职场文书
2015年世界无烟日活动方案
2015/05/04 职场文书
使用Docker容器部署rocketmq单机的全过程
2022/04/03 Servers
大型强子对撞机再次重启探索“第五种自然力”
2022/04/29 数码科技