要求: 1 .用戶加密認證 2 .允許同時多用戶登錄 3 .每個用戶有自己的家目錄,且只能訪問自己的家目錄 4 .對用戶進行磁盤配額,每個用戶的可用空間不同 5 .允許用戶在ftp server上隨意切換目錄 6 .允許用戶查看當前目錄下的文件 7 .允許上傳和下載文件,并保證文件的一致性md5 8 .文件傳輸過程中顯示進度條 9.支持文件的斷點續傳
使用:
1.啟動ftp_server.py
2.創建用戶,輸入:用戶名(默認密碼是zhurui)
3.啟動FTP服務器
4.啟動客戶端ftp_client.py
5.輸入用戶名和密碼:alex zhurui | william zhurui
6.與服務器server交互:
?
server服務端
bin下的文件?
ftp_server.py
# _*_ coding:utf-8 _*_ # Author :simon import os import sys BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath( __file__ ))) sys.path.append(BASE_DIR) from core.main import Manager if __name__ == ' __main__ ' : Manager().run()
conf下的文件
accounts.ini(這個可以在執行中創建)
[william] password = 39da56d2e7a994d38b9aaf329640fc6e homedir = home/ william quota = 10 [zhurui] password = 39da56d2e7a994d38b9aaf329640fc6e homedir = home/ zhurui quota = 10 [simon] password = 39da56d2e7a994d38b9aaf329640fc6e homedir = home/ simon quota = 10
settings.py
# _*_ coding:utf-8 _*_ # Author:Simon # Datetime:2019/8/14 11:00 # Software:PyCharm import os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath( __file__ ))) ACCOUNTS_FILE = os.path.join(BASE_DIR, ' conf ' , ' accounts.ini ' ) HOST = ' 127.0.0.1 ' PORT = 8080 MAX_CONCURRENT_COUNT = 10
core下的文件
main.py
# _*_ coding:utf-8 _*_ # Author:Simon from core.user_handle import UserHandle from core.server import Ftpserver class Manager(): def __init__ (self): pass def start_ftp(self): ''' 啟動ftp_server端 ''' server = Ftpserver() server.run() server.close() def create_user(self): ''' 創建用戶 ''' username = input( ' 請輸入要創建的用戶>: ' ).strip() UserHandle(username).add_user() def quit_func(self): quit( ' get out... ' ) def run(self): msg = ''' \033[31;0m 1、啟動ftp服務器 2、創建用戶 3、退出\033[0m\n ''' msg_dic = { ' 1 ' : ' start_ftp ' , ' 2 ' : ' create_user ' , ' 3 ' : ' quit_func ' } while True: print (msg) num = input( ' 請輸入數字num>>>>: ' ).strip() if num in msg_dic: getattr(self,msg_dic[num])() else : print ( ' \033[1;31m請重新選擇\033[0m ' )
server.py
# -*- coding:utf-8 -*- # Author:Simon # Datetime:2019/8/13 21:02 # Software:PyCharm import os import socket import struct import pickle import hashlib import subprocess import queue from conf import settings # from core.user_handle import UserHandle from core.user_handle import UserHandle from threading import Thread, Lock class Ftpserver(): MAX_SOCKET_LISTEN = 5 MAX_RECV_SIZE = 8192 def __init__ (self): self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.bind((settings.HOST, settings.PORT)) self.socket.listen(self.MAX_SOCKET_LISTEN) self.q = queue.Queue(settings.MAX_CONCURRENT_COUNT) # 可以配置最大并發數 def server_accept(self): ''' 等待client連接 ''' print ( ' starting... ' ) while True: self.conn,self.client_addr = self.socket.accept() print ( ' 客戶端地址: ' , self.client_addr) # pool.submit(self.get_recv, self.conn) # self.server_accept.close() try : # t = Thread(target=self.server_handle, args=(self.conn, )) #報這個錯(TypeError: server_handle() takes 1 positional argument but 2 were given) t = Thread(target=self.server_handle(), args= (self.conn, )) self.q.put(t) t.start() except Exception as e: print (e) self.conn.close() self.q.get() def get_recv(self): ''' 接收client發來的數據 ''' return pickle.loads(self.conn.recv(self.MAX_RECV_SIZE)) def auth(self): ''' 處理用戶的認證請求 1、根據username讀取accounts.ini文件,password相比,判斷用戶是否存在 2、將程序運行的目錄從bin/ftp_server.py修改到用戶home/alice,方便之后查詢ls 3、給client返回用戶的詳細信息 ''' while True: user_dic = self.get_recv() username = user_dic.get( ' username ' ) user_handle = UserHandle(username) user_data = user_handle.judge_user() # 判斷用戶是否存在,返回列表 # 如[('password','202cb962ac59075b964b07152d234b70'),('homedir','home/alex'),('quota','100')] if user_data: if user_data[0][1] == hashlib.md5(user_dic.get( ' password ' ).encode( ' utf-8 ' )).hexdigest(): # 密碼也相同 self.conn.send(struct.pack( ' i ' , 1)) # 登錄成功返回 self.username = username self.homedir_path = ' %s %s %s ' %(settings.BASE_DIR, ' home ' , self.username) os.chdir(self.homedir_path) # 將程序運行的目錄名修改到用戶home目錄下 self.quota_bytes = int(user_data[2][1]) * 1024 * 1024 # 將用戶配額大小從M改到字節 user_info_dic = { ' username ' : username, ' homedir ' : user_data[1][1 ], ' quota ' : user_data[2][1 ] } self.conn.send(pickle.dumps(user_info_dic)) # 用戶的詳細信息發送到客戶端 return True else : self.conn.send(struct.pack( ' i ' , 0)) else : self.conn.send(struct.pack( ' i ' , 0)) def readfile(self): ''' 讀取文件,得到文件內容的bytes型 ''' with open(self.filepath, ' rb ' ) as f: filedata = f.read() return filedata def getfile_md5(self): ''' 對文件內容md5 ''' return hashlib.md5(self.readfile()).hexdigest() def get(self): ''' 從server下載文件到client ''' if len(self.cmds) > 1 : filename = self.cmds[1 ] filepath = os.path.join(os.getcwd(),filename) # os.getcwd()得到當前工作目錄 if os.path.isfile(filepath): # 判斷文件是否存在 exist_file_size = struct.unpack( ' i ' , self.conn.recv(4 ))[0] self.filepath = filepath header_dic = { ' filename ' : filename, ' file_md5 ' : self.getfile_md5(), ' file_size ' : os.path.getsize(self.filepath) } header_bytes = pickle.dumps(header_dic) if exist_file_size: # 表示之前被下載過一部分 self.conn.send(struct.pack( ' i ' , len(header_bytes))) self.conn.send(header_bytes) if exist_file_size != os.path.getsize(self.filepath): with open(self.filepath, ' rb ' ) as f: f.seek(exist_file_size) for line in f: self.conn.send(line) else : print ( ' 斷電和文件本身大小一樣 ' ) else : # 文件第一次下載 self.conn.send(struct.pack( ' i ' , len(header_bytes))) self.conn.send(header_bytes) with open(self.filepath, ' rb ' ) as f: for line in f: self.conn.send(line) else : print ( ' 當前目錄下文件不存在 ' ) self.conn.send(struct.pack( ' i ' ,0)) else : print ( ' 用戶沒用輸入文件名 ' ) def recursion_file(self,menu): ''' 遞歸查詢用戶home/alice目錄下的所有文件,算出文件的大小 ''' res = os.listdir(menu) # 指定目錄下所有的文件和目錄名 for i in res: path = ' %s %s ' % (menu, i) if os.path.isdir(path): # 判斷指定對象是否為目錄 self.recursion_file(path) elif os.path.isfile(path): self.home_bytes_size += os.path.getsize(path) def current_home_size(self): ''' 得到當前用戶home/alice目錄的大小,字節/M ''' self.home_bytes_size = 0 self.recursion_file(self.homedir_path) print ( ' 字節: ' , self.home_bytes_size) # 單位是字節 home_m_size = round(self.home_bytes_size / 1024 /1024, 1 ) print ( ' 單位M: ' , home_m_size) # 單位是: M def put(self): ''' 從client上傳文件到server當前工作目錄下 ''' if len(self.cmds) > 1 : state_size = struct.unpack( ' i ' ,self.conn.recv(4 ))[0] if state_size: self.current_home_size() # 算出了home下已被占用的大小self.home_bytes_size header_bytes = self.conn.recv(struct.unpack( ' i ' , self.conn.recv(4 ))[0]) header_dic = pickle.loads(header_bytes) print (header_dic) filename = header_dic.get( ' filename ' ) file_size = header_dic.get( ' file_size ' ) file_md5 = header_dic.get( ' file_md5 ' ) upload_filepath = os.path.join(os.getcwd(), filename) self.filepath = upload_filepath # 為了全局變量讀取文件算md5時方便 if os.path.exists(upload_filepath): # 文件已經存在 self.conn.send(struct.pack( ' i ' , 1 )) has_size = os.path.getsize(upload_filepath) if has_size == file_size: print ( ' 文件已經存在 ' ) self.conn.send(struct.pack( ' i ' , 0)) else : # 上次沒有傳完,接著繼續傳 self.conn.send(struct.pack( ' i ' , 1 )) if self.home_bytes_size + int(file_size - has_size) > self.quota_bytes: print ( ' 超出了用戶的配額 ' ) self.conn.send(struct.pack( ' i ' , 0)) else : self.conn.send(struct.pack( ' i ' ,1 )) self.conn.send(struct.pack( ' i ' , has_size)) with open(upload_filepath, ' ab ' ) as f: f.seek(has_size) while has_size < file_size: recv_bytes = self.conn.recv(self.MAX_RECV_SIZE) f.write(recv_bytes) has_size += len(recv_bytes) self.conn.send(struct.pack( ' i ' , has_size)) # 為了顯示進度條 if self.getfile_md5() == file_md5: # 判斷下載下來的文件MD5值和server傳過來的MD5值是否一致 print ( ' \033[1;32m上傳成功\033[0m ' ) self.conn.send(struct.pack( ' i ' , 1 )) else : print ( ' \033[1;32m上傳失敗\033[0m ' ) self.conn.send(struct.pack( ' i ' , 0)) else : # 第一次上傳 self.conn.send(struct.pack( ' i ' , 0)) if self.home_bytes_size + int(file_size) > self.quota_bytes: print ( ' \033[1;32m超出了用戶的配額\033[0m ' ) self.conn.send(struct.pack( ' i ' , 0)) else : self.conn.send(struct.pack( ' i ' , 1 )) with open(upload_filepath, ' wb ' ) as f: recv_size = 0 while recv_size < file_size: file_bytes = self.conn.recv(self.MAX_RECV_SIZE) f.write(file_bytes) recv_size += len(file_bytes) self.conn.send(struct.pack( ' i ' , recv_size)) # 為了進度條的顯示 if self.getfile_md5() == file_md5: # 判斷下載下來的文件MD5值和server傳過來的MD5值是否一致 print ( ' \033[1;32m上傳成功\033[0m ' ) self.conn.send(struct.pack( ' i ' , 1 )) else : print ( ' \033[1;32m上傳失敗\033[0m ' ) self.conn.send(struct.pack( ' i ' , 0)) else : print ( ' 待傳的文件不存在 ' ) else : print ( ' 用戶沒有輸入文件名 ' ) def ls(self): ''' 查詢當前工作目錄下,先返回文件列表的大小,再返回查詢的結果 ''' subpro_obj = subprocess.Popen( ' dir ' , shell= True, stdout = subprocess.PIPE, stderr = subprocess.PIPE) stdout = subpro_obj.stdout.read() stderr = subpro_obj.stderr.read() self.conn.send(struct.pack( ' i ' , len(stdout + stderr))) self.conn.send(stdout) self.conn.send(stderr) def mkdir(self): ''' 在當前目錄下,增加目錄 ''' if len(self.cmds) > 1 : mkdir_path = os.path.join(os.getcwd(),self.cmds[1 ]) if not os.path.exists(mkdir_path): # 查看目錄名是否存在 os.mkdir(mkdir_path) print ( ' 增加目錄成功 ' ) self.conn.send(struct.pack( ' i ' , 1)) # 增加目錄成功,返回1 else : print ( ' 目錄名已存在 ' ) self.conn.send(struct.pack( ' i ' , 0)) # 失敗返回0 else : print ( ' 用戶沒有輸入目錄名 ' ) def cd(self): ''' 切換目錄 ''' if len(self.cmds) > 1 : dir_path = os.path.join(os.getcwd(), self.cmds[1 ]) if os.path.isdir(dir_path) : # 查看是否是目錄名 previous_path = os.getcwd() # 拿到當前工作的目錄 os.chdir(dir_path) # 改變工作目錄到 . . . target_dir = os.getcwd() if self.homedir_path in target_dir: # 判斷homedir_path是否在目標目錄 print ( ' 切換成功 ' ) self.conn.send(struct.pack( ' i ' , 1)) # 切換成功返回1 else : print ( ' 切換失敗 ' ) # 切換失敗后,返回到之前的目錄下 os.chdir(previous_path) self.conn.send(struct.pack( ' i ' , 0)) else : print ( ' 要切換的目錄不在該目錄下 ' ) self.conn.send(struct.pack( ' i ' , 0)) else : print ( ' 沒有傳入切換的目錄名 ' ) def remove(self): ''' 刪除指定的文件,或者空文件夾 ''' if len(self.cmds) > 1 : file_name = self.cmds[1 ] file_path = ' %s\%s ' % (os.getcwd(), file_name) if os.path.isfile(file_path): os.remove(file_path) self.conn.send(struct.pack( ' i ' , 1 )) elif os.path.isdir(file_path): # 刪除空目錄 if not len(os.listdir(file_path)): os.removedirs(file_path) print ( ' 刪除成功 ' ) self.conn.send(struct.pack( ' i ' , 1 )) else : print ( ' 文件夾非空,不能刪除 ' ) self.conn.send(struct.pack( ' i ' , 0)) else : print ( ' 不是文件也不是文件夾 ' ) self.conn.send(struct.pack( ' i ' , 0)) else : print ( ' 沒有輸入要刪除的文件 ' ) def server_handle(self): ''' 處理與用戶的交互指令 ''' if self.auth(): print ( ' \033[1;32m用戶登陸成功\033[0m ' ) while True: try : # try ...except 適合windows client斷開 user_input = self.conn.recv(self.MAX_RECV_SIZE).decode( ' utf-8 ' ) # if not user_input: continue #這里適合 linux client斷開 self.cmds = user_input.split() if hasattr(self,self.cmds[0]): getattr(self,self.cmds[0])() else : print ( ' \033[1;31請用戶重復輸入\033[0m ' ) except Exception: break def run(self): self.server_accept() def close(self): self.socket.close() # if __name__ == '__main__': # pool = ThreadPoolExecutor(10)
user_handle.py
# _*_ coding:utf-8 _*_ # Author:Simon # Datetime:2019/8/14 10:26 # Software:PyCharm import configparser import hashlib import os from conf import settings class UserHandle(): def __init__ (self,username): self.username = username self.config = configparser.ConfigParser() # 先生成一個對象 self.config.read(settings.ACCOUNTS_FILE) @property def password(self): ''' 生成用戶的默認密碼 zhurui ''' return hashlib.md5( ' zhurui ' .encode( ' utf-8 ' )).hexdigest() @property def quota(self): ''' 生成每個用戶的磁盤配額 ''' quota = input( ' 請輸入用戶的磁盤配額大小>>>: ' ).strip() if quota.isdigit(): return quota else : exit( ' \033[1;31m磁盤配額必須是整數\033[0m ' ) def add_user(self): ''' 創建用戶,存到accounts.ini ''' if not self.config.has_section(self.username): print ( ' creating username is : ' , self.username) self.config.add_section(self.username) self.config.set(self.username, ' password ' , self.password) self.config.set(self.username, ' homedir ' , ' home/ ' + self.username) self.config.set(self.username, ' quota ' , self.quota) with open(settings.ACCOUNTS_FILE, ' w ' ) as f: self.config.write(f) os.mkdir(os.path.join(settings.BASE_DIR, ' home ' , self.username)) # 創建用戶的home文件夾 print ( ' \033[1;32m創建用戶成功\033[0m ' ) else : print ( ' \033[1;32m用戶已存在\033[0m ' ) def judge_user(self): ''' 判斷用戶是否存在 ''' if self.config.has_section(self.username): return self.config.items(self.username) else : return
client客戶端
download文件是儲存下載的文件;upload是上傳文件的儲存庫(download里邊可以不放東西,等待下載即可;upload里邊放你準備上傳給服務端的文件)
ftp_client.py
# _*_ coding:utf-8 _*_ # Author:Simon # Datetime:2019/8/14 11:12 # Software:PyCharm import os import sys import socket import struct import pickle import hashlib class Ftpclient(): HOST = ' 127.0.0.1 ' # 服務器IP PORT = 8080 # 服務端的端口 MAX_RECV_SIZE = 8192 DOWNLOAD_PATH = os.path.join(os.path.dirname(os.path.abspath( __file__ )), ' download ' ) UPLOAD_PATH = os.path.join(os.path.dirname(os.path.abspath( __file__ )), ' upload ' ) def __init__ (self): self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.connect() def connect(self): ''' 連接服務端server ''' try : self.socket.connect((self.HOST, self.PORT)) except Exception: exit( ' \033[1;31mserver還未啟動\033[0m ' ) def get_recv(self): ''' 獲取server返回的數據 ''' return pickle.loads(self.socket.recv(self.MAX_RECV_SIZE)) def auth(self): ''' 用戶認證 ''' count = 0 while count < 3 : name = input( ' 請輸入用戶名>>: ' ).strip() if not name: continue password = input( ' 請輸入密碼>>: ' ).strip() user_dic = { ' username ' :name, ' password ' :password } self.socket.send(pickle.dumps(user_dic)) # 把用戶名和密碼發送給server res = struct.unpack( ' i ' ,self.socket.recv(4 ))[0] if res: # 接收返回的信息,并判斷 print ( ' welcome ' .center(20, ' - ' )) user_info_dic = self.get_recv() self.username = user_info_dic.get( ' username ' ) print (user_info_dic) return True else : print ( ' \033[1;31m用戶名或者密碼不對!\033[0m ' ) count += 1 def readfile(self): ''' 讀取文件,得到的文件內容的bytes型 ''' with open(self.filepath, ' rb ' ) as f: filedata = f.read() return filedata def getfile_md5(self): ''' 對文件內容md5 ''' return hashlib.md5(self.readfile()).hexdigest() def progress_bar(self, num, get_size, file_size): ''' 進度條顯示 ''' float_rate = get_size / file_size # rate = str(float_rate * 100)[:5] # 95.85% rate = round(float_rate * 100,2) # 95.85% if num == 1: # 1表示下載 sys.stdout.write( ' \r已下載:\033[1;32m{0}%\033[0m ' .format(rate)) elif num == 2: # 2 表示上傳 sys.stdout.write( ' \r已上傳:\033[1;32m{0}%\033[0m ' .format(rate)) sys.stdout.flush() def get(self): ''' 從server下載文件到client ''' if len(self.cmds) > 1 : filename = self.cmds[1 ] self.filepath = os.path.join(self.DOWNLOAD_PATH, filename) # 結合目錄名和文件名 if os.path.isfile(self.filepath): # 如果文件存在,支持斷點續傳 temp_file_size = os.path.getsize(self.filepath) self.socket.send(struct.pack( ' i ' , temp_file_size)) header_size = struct.unpack( ' i ' , self.socket.recv(4 ))[0] if header_size: # 如果存在 header_dic = pickle.loads(self.socket.recv(header_size)) print (header_dic) filename = header_dic.get( ' filename ' ) file_size = header_dic.get( ' file_size ' ) file_md5 = header_dic.get( ' file_md5 ' ) if temp_file_size == file_size: print ( ' \033[1;32m文件已經存在\033[0m ' ) else : print ( ' \033[1;33m正在進行斷點續傳....\033[0m ' ) download_filepath = os.path.join(self.DOWNLOAD_PATH, filename) with open(download_filepath, ' ab ' ) as f: f.seek(temp_file_size) get_size = temp_file_size while get_size < file_size: file_bytes = self.socket.recv(self.MAX_RECV_SIZE) f.write(file_bytes) get_size += len(file_bytes) self.progress_bar( 1, get_size, file_size) # 1表示下載 if self.getfile_md5() == file_md5: # 判斷下載下來的文件MD5值和server傳過來的MD5值是否一致 print ( ' \n\033[1;32m下載成功\033[0m ' ) else : print ( ' \n\033[1;32m下載文件大小與源文件大小不一致,請重新下載,將會支持斷點續傳033[0m ' ) else : print ( ' \n\033[1;31m該文件,之前被下載了一部分,但是server端的該文件,已被刪除,無法再次下載\033[0m ' ) else : # 文件第一次下載 self.socket.send(struct.pack( ' i ' , 0)) # 0 表示之前沒有下載過 header_size = struct.unpack( ' i ' , self.socket.recv(4 ))[0] if header_size: header_dic = pickle.loads(self.socket.recv(header_size)) print (header_dic) filename = header_dic.get( ' filename ' ) file_size = header_dic.get( ' file_size ' ) file_md5 = header_dic.get( ' file_md5 ' ) download_filepath = os.path.join(self.DOWNLOAD_PATH, filename) with open(download_filepath, ' wb ' ) as f: get_size = 0 while get_size < file_size: file_bytes = self.socket.recv(self.MAX_RECV_SIZE) f.write(file_bytes) get_size += len(file_bytes) self.progress_bar( 1, get_size, file_size) # 1表示下載 print ( ' 總大小:%s已下載:%s ' % (file_size, get_size)) if self.getfile_md5() == file_md5: # 判斷下載下來的文件MD5值和server傳過來的MD5值是否一致 print ( ' \n\033[1;32m恭喜你,下載成功\033[0m ' ) else : print ( ' \n\033[1;32m下載失敗,再次下載支持斷點續傳\033[0m ' ) else : print ( ' \n\033[1;32m當前目錄下,文件不存在\033[0m ' ) else : print ( ' 用戶沒有輸入文件名 ' ) def put(self): ''' 往server自己的home/alice目錄下,當前工作的目錄下上傳文件 ''' if len(self.cmds) > 1: # 確保用戶輸入了文件名 filename = self.cmds[1 ] filepath = os.path.join(self.UPLOAD_PATH, filename) if os.path.isfile(filepath): self.socket.send(struct.pack( ' i ' , 1 )) self.filepath = filepath filesize = os.path.getsize(self.filepath) header_dic = { ' filename ' : filename, ' file_md5 ' : self.getfile_md5(), ' file_size ' : filesize } header_bytes = pickle.dumps(header_dic) self.socket.send(struct.pack( ' i ' ,len(header_bytes))) self.socket.send(header_bytes) state = struct.unpack( ' i ' , self.socket.recv(4 ))[0] if state: # 已經存在了 has_state = struct.unpack( ' i ' , self.socket.recv(4 ))[0] if has_state: quota_state = struct.unpack( ' i ' ,self.socket.recv(4 ))[0] if quota_state: has_size = struct.unpack( ' i ' , self.socket.recv(4 ))[0] with open(self.filepath, ' rb ' ) as f: f.seek(has_size) for line in f: self.socket.send(line) recv_size = struct.unpack( ' i ' , self.socket.recv(4 ))[0] self.progress_bar( 2 , recv_size, filesize) success_state = struct.unpack( ' i ' , self.socket.recv(4 ))[0] ''' 這里一定要判斷,因為最后一次send(line)之后等待server返回, server返回,最后一次的recv_size==file_size,但client已經跳出了循環, 所以在for外面接收的success_state其實時file_size,這種情況只針對大文件 ''' if success_state == filesize: success_state = struct.unpack( ' i ' , self.socket.recv(4 ))[0] if success_state: print ( ' \n\033[1;32m恭喜您,上傳成功\033[0m ' ) else : print ( ' \n\033[1;32m上傳失敗\033[0m ' ) else : # 超出了配額 print ( ' \033[1;31m超出了用戶的配額\033[0m ' ) else : # 存在的大小和文件大小一致,不必再傳 print ( ' \033[1;31m當前目錄下,文件已經存在\033[0m ' ) else : # 第一次傳 quota_state = struct.unpack( ' i ' , self.socket.recv(4 ))[0] if quota_state: with open(self.filepath, ' rb ' ) as f: send_bytes = b '' for line in f: self.socket.send(line) send_bytes += line print ( ' 總大小:%s 已上傳:%s ' % (filesize, len(send_bytes))) recv_size = struct.unpack( ' i ' ,self.socket.recv(4 ))[0] self.progress_bar( 2 , recv_size, filesize) succes_state = struct.unpack( ' i ' ,self.socket.recv(4 ))[0] if succes_state == filesize: succes_state = struct.unpack( ' i ' ,self.socket.recv(4 ))[0] if succes_state: print ( ' \n\033[1;32m恭喜您,上傳成功\033[0m ' ) else : print ( ' \n\033[1;32m上傳失敗\033[0m ' ) else : # 超出了配額 print ( ' \033[1;31m超出了用戶的配額\033[0m ' ) else : # 文件不存在 print ( ' \033[1;31m文件不存在\033[0m ' ) self.socket.send(struct.pack( ' i ' , 0)) else : print ( ' 用戶沒有輸入文件名 ' ) def ls(self): ''' 查詢當前工作目錄下,文件列表 ''' dir_size = struct.unpack( ' i ' , self.socket.recv(4 ))[0] recv_size = 0 recv_bytes = b '' while recv_size < dir_size: temp_bytes = self.socket.recv(self.MAX_RECV_SIZE) recv_bytes += temp_bytes recv_size += len(temp_bytes) print (recv_bytes.decode( ' gbk ' )) # gbk適合windows,utf-8適合linux def mkdir(self): ''' 增加目錄 ''' if len(self.cmds) > 1 : res = struct.unpack( ' i ' , self.socket.recv(4 ))[0] if res: print ( ' \033[1;32m在當前目錄下,增加目錄: %s 成功\033[0m ' % self.cmds[1 ]) else : print ( ' \033[1;31m增加目錄失敗\033[0m ' ) else : print ( ' 沒有輸入要增加的目錄名 ' ) def cd(self): ''' 切換目錄 ''' if len(self.cmds) > 1 : res = struct.unpack( ' i ' , self.socket.recv(4 ))[0] if res: print ( ' \033[1;32m切換成功\033[0m ' ) else : print ( ' \033[1;32m切換失敗\033[0m ' ) else : print ( ' 沒有輸入要切換的目錄名 ' ) def remove(self): ''' 刪除指定的文件,或者文件夾 ''' if len(self.cmds) > 1 : res = struct.unpack( ' i ' , self.socket.recv(4 ))[0] if res: print ( ' \033[1;32m刪除成功\033[0m ' ) else : print ( ' \033[1;31m刪除失敗\033[0m ' ) else : print ( ' 沒有輸入要刪除的文件 ' ) def interactive(self): ''' 與server交互 ''' if self.auth(): while True: try : user_input = input( ' [%s]>>>: ' % self.username) if not user_input: continue self.socket.send(user_input.encode( ' utf-8 ' )) self.cmds = user_input.split() if hasattr(self, self_cmds[0]): getattr(self, self.cmds[0])() else : print ( ' 請重新輸入 ' ) except Exception as e: # server關閉了 print (e) break def close(self): self.socket.close() if __name__ == ' __main__ ' : ftp_client = Ftpclient() ftp_client.interactive() ftp_client.close()
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
