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中映射类型的内建函数和工厂函数
Aug 19 Python
轻松掌握python设计模式之访问者模式
Nov 18 Python
python 读取DICOM头文件的实例
May 07 Python
python 删除字符串中连续多个空格并保留一个的方法
Dec 22 Python
利用ctypes获取numpy数组的指针方法
Feb 12 Python
Python 使用 PyMysql、DBUtils 创建连接池提升性能
Aug 14 Python
django rest framework 自定义返回方式
Jul 12 Python
浅谈Python 钉钉报警必备知识系统讲解
Aug 17 Python
Python 整行读取文本方法并去掉readlines换行\n操作
Sep 03 Python
利用Python实现学生信息管理系统的完整实例
Dec 30 Python
Python爬虫入门案例之回车桌面壁纸网美女图片采集
Oct 16 Python
Python实现灰色关联分析与结果可视化的详细代码
Mar 25 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实现二分查找算法代码分享
2011/06/24 PHP
php pdo操作数据库示例
2017/03/10 PHP
safari下载文件自动加了html后缀问题
2018/11/09 PHP
php使用socket调用http和smtp协议实例小结
2019/07/26 PHP
OfflineSave离线保存代码再次发布使用说明
2007/05/23 Javascript
Javascript 表单之间的数据传递代码
2008/12/04 Javascript
jQuery对象与DOM对象之间的转换方法
2010/04/15 Javascript
JS关闭窗口与JS关闭页面的几种方法小结
2013/12/17 Javascript
浅谈JavaScript Array对象
2014/12/29 Javascript
js实现仿百度风云榜可重复多次调用的TAB切换选项卡效果
2015/08/31 Javascript
jQuery日程管理插件fullcalendar使用详解
2017/01/07 Javascript
Angularjs中数据绑定的实例详解
2017/08/25 Javascript
[js高手之路]原型式继承与寄生式继承详解
2017/08/28 Javascript
vue中的计算属性实例详解
2018/09/19 Javascript
使用Vue CLI创建typescript项目的方法
2019/08/09 Javascript
react native 仿微信聊天室实例代码
2019/09/17 Javascript
如何在selenium中使用js实现定位
2020/08/18 Javascript
[08:07]DOTA2每周TOP10 精彩击杀集锦vol.8
2014/06/25 DOTA
Python警察与小偷的实现之一客户端与服务端通信实例
2014/10/09 Python
Python实现的批量下载RFC文档
2015/03/10 Python
python通过pil模块获得图片exif信息的方法
2015/03/16 Python
django 常用orm操作详解
2017/09/13 Python
python自动化工具之pywinauto实例详解
2019/08/26 Python
Python使用Pandas库常见操作详解
2020/01/16 Python
keras使用Sequence类调用大规模数据集进行训练的实现
2020/06/22 Python
Python实现JS解密并爬取某音漫客网站
2020/10/23 Python
python反扒机制的5种解决方法
2021/02/06 Python
英国最大的在线蜡烛商店:Candles Direct
2019/03/26 全球购物
联想智利官方网站:Lenovo Chile
2020/06/03 全球购物
幼教毕业生自我鉴定
2014/01/12 职场文书
领导干部考察材料
2014/02/08 职场文书
厨师个人自我鉴定范文
2014/04/19 职场文书
2014党员学习兰辉先进事迹思想汇报
2014/09/17 职场文书
先进个人评语大全
2015/01/04 职场文书
pycharm debug 断点调试心得分享
2021/04/16 Python
Java异常体系非正常停止和分类
2022/06/14 Java/Android