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 02 Python
python插入排序算法的实现代码
Nov 21 Python
Python入门篇之正则表达式
Oct 20 Python
Python备份目录及目录下的全部内容的实现方法
Jun 12 Python
Python Socket传输文件示例
Jan 16 Python
python中使用正则表达式的连接符示例代码
Oct 10 Python
pytorch标签转onehot形式实例
Jan 02 Python
Python基于smtplib模块发送邮件代码实例
May 29 Python
python 解决Windows平台上路径有空格的问题
Nov 10 Python
python BeautifulSoup库的安装与使用
Dec 17 Python
python - timeit 时间模块
Apr 06 Python
Python面向对象编程之类的概念
Nov 01 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+MYSQL开发工具及资源收藏
2007/01/02 PHP
php批量缩放图片的代码[ini参数控制]
2011/02/11 PHP
php中current、next与reset函数用法实例
2014/11/17 PHP
PHP加密解密类实例分析
2015/04/20 PHP
smarty的section嵌套循环用法示例
2016/05/28 PHP
PHP处理CSV表格文件的常用操作方法总结
2016/07/01 PHP
php实现登录tplink WR882N获取IP和重启的方法
2016/07/20 PHP
php字符串操作针对负值的判断分析
2016/07/28 PHP
Javascript 获取链接(url)参数的方法[正则与截取字符串]
2010/02/09 Javascript
JQuery 自定义CircleAnimation,Animate方法学习笔记
2011/07/10 Javascript
JSON+JavaScript处理JSON的简单例子
2013/03/20 Javascript
jquery单行文字向上滚动效果示例
2014/03/06 Javascript
浅谈javascript中字符串String与数组Array
2014/12/31 Javascript
js简单抽奖代码
2015/01/16 Javascript
JS实现霓虹灯文字效果的方法
2015/08/06 Javascript
跟我学习javascript的定时器
2015/11/19 Javascript
js判断所有表单项不为空则提交表单的实现方法
2016/09/09 Javascript
Vue2.0+ElementUI实现表格翻页的实例
2017/10/23 Javascript
PHP自动加载autoload和命名空间的应用小结
2017/12/01 Javascript
简单的三步vuex入门
2018/05/20 Javascript
javascript中的event loop事件循环详解
2018/12/14 Javascript
es6 super关键字的理解与应用实例分析
2020/02/15 Javascript
[01:22:10]Ti4 循环赛第二日 DK vs Empire
2014/07/11 DOTA
Python内置的字符串处理函数整理
2013/01/29 Python
用python求一个数组的和与平均值的实现方法
2019/06/29 Python
Python可变对象与不可变对象原理解析
2020/02/25 Python
基于Python和C++实现删除链表的节点
2020/07/06 Python
python分布式爬虫中消息队列知识点详解
2020/11/26 Python
西班牙土拨鼠床垫公司,感觉在云端:Marmota
2019/03/18 全球购物
拉夫劳伦爱尔兰官方网站:Ralph Lauren爱尔兰
2020/04/10 全球购物
安踏广告词改编版
2014/03/21 职场文书
明信片寄语大全
2014/04/08 职场文书
2014年学生会部门工作总结
2014/11/07 职场文书
小孩不笨观后感
2015/06/03 职场文书
父亲节感言
2015/08/03 职场文书
《莫泊桑拜师》教学反思
2016/02/22 职场文书