python 基于selectors庫實現(xiàn)文件上傳與下載
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 = {} # 創(chuàng)建空字典
self.hasReceived = 0
self.hasSend=0
self.sel = selectors.DefaultSelector() # 生成一個select對象
self.create_socket() #create_socket()是創(chuàng)建socket對象函數(shù)完成綁定功能
self.hanle() #handle()函數(shù)完成循環(huán)監(jiān)聽
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函數(shù)處理
print("服務(wù)端已打開,請連接客戶端")
def hanle(self):
while True:
events = self.sel.select() # 默認(rèn)是阻塞,有活動連接就返回活動的連接列表
# 這里看起來是select,其實有可能會使用epoll,如果你的系統(tǒng)支持epoll,那么默認(rèn)就是epoll
# print("event==",events)
for key, mask in events:
callback = key.data # 去調(diào)accept函數(shù)
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) # 設(shè)定非阻塞
self.sel.register(conn, selectors.EVENT_READ, self.read) # 新連接注冊read回調(diào)函數(shù)
self.dic[conn] = {} # 在空字典里進(jìn)行了conn賦值,self.dic={conn:{},}
def read(self, conn, mask): # 接收了conn和mask
try: # 加異常防止客戶端突然斷開
if not self.dic[conn]: # 判斷self.dic[conn]里面是否是空字典,如果是空字典,代表第一次進(jìn)來
print('====第一次進(jìn)來')
data = conn.recv(1024) # conn接收了客戶端發(fā)來的數(shù)據(jù)
print("data==",str(data, encoding='utf-8'))
cmd, filename,filesize = str(data, encoding='utf-8').split('|') # 把接收到客戶端發(fā)來的包解開拿到cmd,filename,filesize個信息
self.dic = {conn: {"cmd": cmd, "filename": filename, "filesize": int(filesize)}} # 把拿到的cmd,filename,filesize信息放到self.dic字典里去后程序返回到handle()函數(shù)里的events繼續(xù)監(jiān)聽
print(self.dic)
if cmd == 'put': # 如果接收的信息是put
conn.send(bytes("OK", encoding='utf8')) # 給客戶端返回一條數(shù)據(jù)
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: # 如果不是空字典代表不是第一次進(jìn)來
print('不是第一次來的')
print(self.dic)
if self.dic[conn].get('cmd', None): # 對接收的命令進(jìn)行分發(fā)判斷是put還是get
cmd = self.dic[conn].get('cmd')
if hasattr(self, cmd): # 如果cmd=put調(diào)用put函數(shù),如果是cmd=get函數(shù)調(diào)用get函數(shù)
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) # 如果沒有接收到數(shù)據(jù)做一個關(guān)閉解除
conn.close()
# put上傳函數(shù)
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) # 接收客戶端上傳的數(shù)據(jù)1024字節(jié)
self.hasReceived += len(recv_data) # 把接收的數(shù)據(jù)累加到變量self.hasReceived
with open(path, 'ab') as f: # 打開文件
f.write(recv_data) # 把接收的數(shù)據(jù)寫到文件里去
if fileSize == self.hasReceived: # 判斷文件大小跟接收大小是否一樣
if conn in self.dic.keys(): # 如果文件大小跟接收大小一樣清空字典
self.dic[conn] = {}
self.hasReceived = 0 #S上傳結(jié)束之后,需要將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接收了客戶端發(fā)來的數(shù)據(jù)
dataOK = str(data, encoding='utf-8')
if dataOK == 'OK':
with open(file, 'rb') as f: # 打開文件
while fileSize > self.hasSend: # 循環(huán)的發(fā)送文件給客戶端
contant = f.read(1024)
recv_size = len(contant)
conn.send(contant)
self.hasSend += recv_size
s = str(int(self.hasSend / fileSize * 100)) + "%"
print("正在下載文件: " + fileName + " 已經(jīng)下載:" + 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在命令行輸入的參數(shù),第一個參數(shù)默認(rèn)文件名,第二個參數(shù)跟IP地址和端口
if len(self.args)>1: #如果大于1把第二個參數(shù)倆個值賦值給port
self.port=(self.args[1],int(self.args[2]))
else:
self.port=("127.0.0.1",8899) #如果沒有第二個參數(shù)默認(rèn)取這個
self.create_socket() #
self.command_fanout() #進(jìn)行命令分發(fā)
self.mainPath = os.path.join(BASE_DIR, 'filename') # 獲取該客戶端下的filename路徑
#create_socket函數(shù)創(chuàng)建socket對象連接服務(wù)端
def create_socket(self):
try:
self.sk = socket.socket()
self.sk.connect(self.port)
print('連接FTP服務(wù)器成功!')
except Exception as e:
print("eroor:",e)
#command_fanout()函數(shù)進(jìn)行命令分發(fā)
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("是否繼續(xù)進(jìn)入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關(guān)閉了
print('%s' % e)
break
def help_info(self):
print ('''
get + (文件名) 表示下載文件
put + (文件名) 表示上傳文件
quit 表示退出登錄
''')
#put()上傳函數(shù)
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')) #調(diào)用send方法把fileInf發(fā)給服務(wù)端
recvStatus = self.sk.recv(1024) #接收服務(wù)端返回的OK內(nèi)容
print('recvStatus' , recvStatus)
hasSend = 0
if str(recvStatus, encoding='utf8') == "OK": #如果接收到服務(wù)端返回的OK
with open(file, 'rb') as f: #打開文件
while fileSize > hasSend : #循環(huán)的去上傳文件
contant = f.read(1024)
recv_size = len(contant)
self.sk.send(contant)
hasSend += recv_size
s=str(int(hasSend/fileSize*100))+"%"
print("正在上傳文件: "+fileName+" 已經(jīng)上傳:" +s)
print('%s文件上傳完畢' % (fileName,))
else:
print('要上傳的文件不存在')
#get()下載函數(shù)
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')) # 調(diào)用send方法把fileInfo發(fā)給服務(wù)端
recvdata = self.sk.recv(1024) # 接收服務(wù)端返回的是否存在文件內(nèi)容
recvStatus, fileSize = str(recvdata, encoding='utf-8').split('|')
print("recvStatus==",recvStatus,fileSize)
fileSize = int(fileSize)
hasReceived = 0
if recvStatus == "YES": # 如果接收到服務(wù)端返回的YES
self.sk.send(bytes('OK', encoding='utf8')) # 通知服務(wù)端可以正常下載了
while fileSize > hasReceived: # 循環(huán)的發(fā)送文件給客戶端
recv_data = self.sk.recv(1024) # 接收客戶端上傳的數(shù)據(jù)1024字節(jié)
hasReceived += len(recv_data) # 把接收的數(shù)據(jù)累加到變量self.hasReceived
print("hasReceived",hasReceived)
with open(path, 'ab') as f: # 打開文件
f.write(recv_data) # 把接收的數(shù)據(jù)寫到文件里去
if fileSize == hasReceived: # 判斷文件大小跟接收大小是否一樣
print("%s 下載完畢!" % fileName)
recvStatus = 'YESS'
else:
print('要下載的文件不存在')
if __name__=='__main__':
selectFtpClient()
以上就是python 基于selectors庫實現(xiàn)文件上傳與下載的詳細(xì)內(nèi)容,更多關(guān)于python 上傳下載的資料請關(guān)注腳本之家其它相關(guān)文章!
- 用Python自動下載網(wǎng)站所有文件
- python基于paramiko庫遠(yuǎn)程執(zhí)行 SSH 命令,實現(xiàn) sftp 下載文件
- Python解析m3u8拼接下載mp4視頻文件的示例代碼
- python爬蟲智能翻頁批量下載文件的實例詳解
- python 下載文件的幾種方法匯總
- Python實現(xiàn)FTP文件定時自動下載的步驟
- Python 使用SFTP和FTP實現(xiàn)對服務(wù)器的文件下載功能
- python從ftp獲取文件并下載到本地
- Python根據(jù)URL地址下載文件并保存至對應(yīng)目錄的實現(xiàn)
- python 下載文件的幾種方式分享
相關(guān)文章
python ftp 按目錄結(jié)構(gòu)上傳下載的實現(xiàn)代碼
這篇文章主要介紹了python ftp 按目錄結(jié)構(gòu)上傳下載的實現(xiàn)代碼,需要的朋友可以參考下2018-09-09
python使用PyV8執(zhí)行javascript代碼示例分享
這篇文章主要介紹了python使用PyV8執(zhí)行javascript的小示例,大家參考使用吧2013-12-12
Python+Pandas實現(xiàn)數(shù)據(jù)透視表
對于數(shù)據(jù)透視表,相信對于Excel比較熟悉的小伙伴都知道如何使用它。本文將利用Python Pandas實現(xiàn)數(shù)據(jù)透視表功能,感興趣的可以學(xué)習(xí)一下2022-06-06
在python中利用dict轉(zhuǎn)json按輸入順序輸出內(nèi)容方式
今天小編就為大家分享一篇在python中利用dict轉(zhuǎn)json按輸入順序輸出內(nèi)容方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-02-02
python3 循環(huán)讀取excel文件并寫入json操作
這篇文章主要介紹了python3 循環(huán)讀取excel文件并寫入json操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-07-07
Python 數(shù)據(jù)科學(xué) Matplotlib圖庫詳解
Matplotlib 是 Python 的二維繪圖庫,用于生成符合出版質(zhì)量或跨平臺交互環(huán)境的各類圖形。今天通過本文給大家分享Python 數(shù)據(jù)科學(xué) Matplotlib的相關(guān)知識,感興趣的朋友一起看看吧2021-07-07
Python爬蟲爬取美劇網(wǎng)站的實現(xiàn)代碼
一直在學(xué)習(xí)Python爬蟲,所以今天就心血來潮來寫了個爬蟲,抓取該網(wǎng)站上所有美劇鏈接,并保存在文本文檔中,想要哪部劇就直接打開復(fù)制鏈接到迅雷就可以下載啦2016-09-09
python的簡單web框架flask快速實現(xiàn)詳解
這篇文章主要為大家介紹了python的簡單web框架flask快速實現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
jupyter notebook讀取/導(dǎo)出文件/圖片實例
這篇文章主要介紹了jupyter notebook讀取/導(dǎo)出文件/圖片實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-04-04

