目的:實現客戶端到伺服器的上傳功能

所需資料夾:

客戶端是FTP_client             服務端是FTP_server

bin檔案存放執行檔案,conf下存放配置檔案,core下是核心檔案,home是家目錄,logger存放日誌檔案.

客戶端程式碼    ftp_client.py:

import optparse
import socket
import os,sys
import json
import time
import hashlib STATUS_CODE = {
250: "Invalid cmd format,e.g:{'action':'get','filname':'test.py','size':344}",
251: "Incalid cmd",
252: "Invalid auth data",
253: "Wrong username or password",
254: "Passed authentication",
255: "Filename doesn't provided",
256: "Filename doesn't exist on the server",
257: "Ready to send file",
258: "md5 verification", 800: "The file exist,but not enough,is continue?",
801: "The file exist!",
802: "Ready to receive datas", 900: "md5 validate success"
} class ClientHandler():
def __init__(self):
self.op = optparse.OptionParser()
self.op.add_option("-s","--server",dest="server")
self.op.add_option("-P","--port",dest="port")
self.op.add_option("-u","--username",dest="username")
self.op.add_option("-p","--password",dest="password") self.options,self.args = self.op.parse_args()
self.verify_args(self.options,self.args)
self.make_connection()
self.mainPath = os.path.dirname(os.path.abspath(__file__))
self.last = 0 def verify_args(self,options,args):
server = options.server
port = options.port
username = options.username
password = options.password
if int(port)>0 and int(port)<65535:
return True
else:
exit("The port is in 0~65535") def make_connection(self):
self.sock = socket.socket()
self.sock.connect((self.options.server,int(self.options.port))) def interactive(self):
print("Begin to interactive...")
if self.authenticate():
while True:
cmd_info = input("[%s]"%self.current_dir).strip()
cmd_list = cmd_info.split()
if hasattr(self,cmd_list[0]):
func = getattr(self,cmd_list[0])
func(*cmd_list) def put(self,*cmd_list):
# put 12.jpg images
action,local_path,target_path = cmd_list
local_path=os.path.join(self.mainPath,local_path) file_name = os.path.basename(local_path)
file_size = os.stat(local_path).st_size data = {
"action": "put",
"file_name": file_name,
"file_size": file_size,
"target_path": target_path
} self.sock.send(json.dumps(data).encode('utf-8')) is_exist = self.sock.recv(1024).decode('utf-8')
######################
has_sent = 0
if is_exist=="":
#檔案完整
choice = input("The file exist,but not enough,is continue?[Y/N]").strip()
if choice.upper()=='Y':
self.sock.sendall("Y".encode('utf-8'))
continue_position = self.sock.recv(1024).decode('utf-8')
has_sent+=int(continue_position) else:
self.sock.sendall("N".encode('utf-8')) elif is_exist=="":
#檔案完全存在
print('The file exist')
return f = open(local_path,"rb")
f.seek(has_sent)
start = time.time()
md5_obj = hashlib.md5()
while has_sent<file_size:
data = f.read(1024)
self.sock.sendall(data)
has_sent+=len(data)
md5_obj.update(data)
self.show_process(has_sent,file_size)
else:
print("post success!!!")
md5_val = md5_obj.hexdigest()
md5_server = self.sock.recv(1024).decode('utf-8')
print(md5_server)
self.sock.sendall(md5_val.encode('utf-8')) f.close()
print("Put Success") def show_process(self,has,total):
rate = float(has)/float(total)
rate_num = int(rate*100)
rate_num_1 = int(rate_num/2)
if self.last!=rate_num:
sys.stdout.write("%s%% %s\r"%(rate_num,"#"*rate_num_1))
self.last = rate_num def authenticate(self):
if self.options.username is None or self.options.password is None:
username = input('username: ')
password = input('password: ')
return self.get_auth_result(username,password) return self.get_auth_result(self.options.username,self.options.password) def response(self):
data = self.sock.recv(1024).decode('utf-8')
data = json.loads(data)
return data def get_auth_result(self,user,pwd):
data = {
"action": "auth",
"username": user,
"password": pwd
}
self.sock.send(json.dumps(data).encode('utf-8'))
response = self.response()
print(response)
if response["status_code"]==254:
self.user = user
self.current_dir = user
print(STATUS_CODE[254])
return True
else:
print(STATUS_CODE[response["status_code"]]) def ls(self,*cmd_list):
data = {
"action":"ls",
}
self.sock.sendall(json.dumps(data).encode('utf-8'))
data = self.sock.recv(1024).decode('utf-8')
print(data) def cd(self,*cmd_list):
data = {
"action": "cd",
"dirname": cmd_list[1],
}
self.sock.send(json.dumps(data).encode('utf-8')) data = self.sock.recv(1024).decode('utf-8')
self.current_dir = self.user + "\\" +os.path.basename(data) ch = ClientHandler()
ch.interactive()

服務端程式碼      啟動程式碼ftp_server.py

import os,sys
BaseAddress = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BaseAddress)
from core import main
if __name__ == '__main__':
main.ArgvHandler()

核心程式碼     main.py

import optparse
import socketserver
from conf import settings
from core import server class ArgvHandler():
def __init__(self):
self.op = optparse.OptionParser()
# self.op.add_option('-s','--server',dest='server')
# self.op.add_option('-P','--port',dest='port')
# self.op.add_option('-s','--s',dest='server') options,args = self.op.parse_args()
self.verify_args(options,args) def verify_args(self,options,args):
cmd = args[0]
if hasattr(self,cmd):
func = getattr(self,cmd)
func() def start(self):
print('the server is working...')
s = socketserver.ThreadingTCPServer((settings.IP,settings.PORT),server.SeverHandler)
s.serve_forever() def help(self):
pass

執行程式碼

import socketserver
import json
import configparser
from conf import settings
import os
import hashlib STATUS_CODE = {
250: "Invalid cmd format,e.g:{'action':'get','filname':'test.py','size':344}",
251: "Incalid cmd",
252: "Invalid auth data",
253: "Wrong username or password",
254: "Passed authentication",
255: "Filename doesn't provided",
256: "Filename doesn't exist on the server",
257: "Ready to send file",
258: "md5 verification", 800: "The file exist,but not enough,is continue?",
801: "The file exist!",
802: "Ready to receive datas", 900: "md5 validate success"
} class SeverHandler(socketserver.BaseRequestHandler):
def handle(self):
while 1:
data = self.request.recv(1024).strip()
data = json.loads(data.decode('utf-8')) if data.get("action"):
if hasattr(self,data.get("action")):
func = getattr(self,data.get("action"))
func(**data)
else:
print('Invalid cmd') def send_response(self,state_code):
response = {"status_code":state_code,'status_mes':STATUS_CODE[state_code]}
self.request.sendall(json.dumps(response).encode('utf-8')) def auth(self,**data):
username = data['username']
password = data['password'] user = self.authenticate(username,password) if user:
self.send_response(254)
else:
self.send_response(253) def authenticate(self,user,pwd):
config = configparser.ConfigParser()
config.read(settings.ACCOUNT_PATH) if user in config.sections():
print("================================") if config[user]["Password"]==pwd:
self.user = user
self.mainPath = os.path.join(settings.BASE_DIR,"home",self.user) print("passed authentication")
return self.user def put(self,**data):
print("data: ",data)
file_name = data.get('file_name')
file_size = data.get('file_size')
target_path = data.get('target_path') abs_path = os.path.join(self.mainPath,target_path,file_name) #####################
has_received = 0
if os.path.exists(abs_path):
file_has_size = os.stat(abs_path).st_size
if file_has_size<file_size:
#斷點續傳
self.request.sendall("".encode('utf-8'))
choice = self.request.recv(1024).decode('utf-8')
if choice=="Y":
self.request.sendall(str(file_has_size).encode('utf-8'))
has_received+=file_has_size
f = open(abs_path,"ab")
else:
f = open(abs_path,'wb') else:
#檔案完全存在
self.request.sendall("".encode('utf-8'))
return else:
self.request.sendall("".encode('utf-8'))
f = open(abs_path,"wb") md5_obj = hashlib.md5()
while has_received<file_size:
try:
data = self.request.recv(1024)
md5_obj.update(data)
except Exception as e:
break
f.write(data)
has_received+=len(data)
md5_val = md5_obj.hexdigest()
self.request.sendall(md5_val.encode('utf-8'))
md5_client = self.request.recv(1024)
print(md5_client)
f.close() def ls(self,**data):
file_list = os.listdir(self.mainPath)
file_str = "\n".join(file_list)
if not len(file_list):
file_str="<empty>"
self.request.sendall(file_str.encode('utf-8')) def cd(self,**data):
dirname = data.get("dirname")
if dirname=="..":
self.mainPath = os.path.dirname(self.mainPath)
else:
self.mainPath = os.path.join(self.mainPath,dirname) self.request.sendall(self.mainPath.encode('utf-8'))

到此為止,可以簡單的完成上傳任務,如果需要新增任務,只需要在ftp_client.py和server.py下面定義新的函式進行互動就可以了。