python 基於selectors庫實現檔案上傳與下載
阿新 • • 發佈:2021-01-04
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,self.read) # 新連線註冊read回撥函式 self.dic[conn] = {} # 在空字典裡進行了conn賦值,self.dic={conn:{},} def read(self,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,filesize個資訊 self.dic = {conn: {"cmd": cmd,"filename": filename,"filesize": int(filesize)}} # 把拿到的cmd,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,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,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,fileName): path = os.path.join(BASE_DIR,"download",fileName) # 拿到要接收的資訊 fileSize=0 fileInfo = '%s|%s|%s' % (cmd,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 上傳下載的資料請關注我們其它相關文章!