python基础之FTP

目的:实现客户端到服务器的上传功能

所需文件夹:

客户端是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=="800":
            #文件完整
            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=="801":
            #文件完全存在
            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()
View Code

服务端代码      启动代码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()
View Code

核心代码     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
View Code

执行代码

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("800".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("801".encode('utf-8'))
                return

        else:
            self.request.sendall("802".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'))
View Code

到此为止,可以简单的完成上传任务,如果需要添加任务,只需要在ftp_client.py和server.py下面定义新的函数进行交互就可以了。

猜你喜欢

转载自www.cnblogs.com/smart-zihan/p/9614771.html