• 作业:开发支持多用户在线FTP程序


    作业:开发支持多用户在线FTP程序
     
     要求:
      用户加密认证;
      允许同时多用户登陆;
      每个用户有自己的家目录,且只能访问自己的家目录;
      对用户进行磁盘配额,每个用户的可用空间不同;
      允许用户在ftp server上随意切换目录;
      允许用户查看当前目录下的文件;
      允许上传和现在文件,保证文件一致性;
      文件传输过程中显示进度条;
      附加功能:支持文件断点续传;


    服务器端系统程序目录
    ├── bin
    │   ├── ftp_server.py
    │   └── __init__.py
    ├── conf
    │   ├── __init__.py
    │   └── user_data
    │       ├── brace.json
    │       ├── create_user.py
    │       ├── __init__.py
    │       └── kitty.json
    ├── core
    │   ├── comman_func.py
    │   ├── __init__.py
    │   └── __pycache__
    │       ├── comman_func.cpython-36.pyc
    │       └── __init__.cpython-36.pyc
    ├── home
    │   ├── brace
    │   ├── __init__.py
    │   └── kitty
    ├── __init__.py
    └── logs
        └── __init__.py

    1.  ftp_server.py      主程序:

    # Author:Brace Li
    import socketserver, json, os, sys, shutil
    import hashlib
    # load parents directory into environment
    base_dir = os.path.dirname(os.path.dirname(os.path.abspath("__file__")))
    sys.path.append(base_dir)
    from core import comman_func
    class MyTCPHandler(socketserver.BaseRequestHandler):
        def auth(self, *args, **kwargs):
            """
            for user authentication;
            :param user_info:
            :return:
            """
            user_info = args[0]
            confirm_data = {
                "confirm_status": False,
                "user_name": user_info["username"],
                "home_path": "",
                "max_size": "",
                "used_size": ""
            }
            print("Client: ", user_info)
            user_file = comman_func.get_path(user_info["username"], "user")
            user_dir = comman_func.get_path(user_info["username"], "data")
            print("file: ", user_file)
            print("dir: ", user_dir)
            if os.path.isfile(user_file):
                server_user = comman_func.get_data(user_info["username"])
                print("Server: ", server_user)
                if user_info["userpassword"] == server_user["user_password"] and server_user["status"]:
                    confirm_data = {
                        "confirm_status": True,
                        "user_name": user_info["username"],
                        "home_path": user_dir,
                        "max_size": server_user["max_size"],
                        "used_size": server_user["used_size"]
                    }
                    self.user = user_info["username"]
            self.request.send(json.dumps(confirm_data).encode("utf-8"))
        def check_dir(self, *args, **kwargs):
            new_dir = comman_func.path_combine(args[0]["current_dir"], args[0]["new_dir"])
            print("args:", args[0])
            print(new_dir)
            if os.path.isdir(new_dir):
                confirm_data = {"confirm_status": True}
            else:
                confirm_data = {"confirm_status": False}
            self.request.send(json.dumps(confirm_data).encode("utf-8"))
        def ls(self, *args, **kwargs):
            current_dir = args[0]["current_dir"]
            data = os.listdir(current_dir)
            self.request.send(json.dumps(data).encode("utf-8"))
        def mkdir(self, *args, **kwargs):
            new_dir = comman_func.path_combine(args[0]["current_dir"], args[0]["new_dir"])
            print(new_dir)
            os.makedirs(new_dir)
            data = [True, ]
            self.request.send(json.dumps(data).encode("utf-8"))
        def rm(self, *args, **kwargs):
            rm_dir = comman_func.path_combine(args[0]["current_dir"], args[0]["rm_dir"])
            print(rm_dir)
            if os.path.isdir(rm_dir):
                shutil.rmtree(rm_dir)
            else:
                os.remove(rm_dir)
            data = [True, ]
            self.request.send(json.dumps(data).encode("utf-8"))
        def put(self, *args, **kwargs):
            """
            put file to ftp server
            :param cmd_action:
            :return:
            """
            total_size = comman_func.calculate_size(self.user)
            user_data = comman_func.get_data(self.user)
            user_data["used_size"] = total_size
            file_name = comman_func.path_combine(args[0]["file_dir"], args[0]["file_name"])
            file_size = args[0]["file_size"]
            balance_size = user_data["max_size"] - user_data["used_size"]
            response_data = {"balance_size": balance_size}
            if os.path.isfile(file_name):
                response_data["file_status"] = True
            else:
                response_data["file_status"] = False
            self.request.send(json.dumps(response_data).encode("utf-8"))
            if balance_size > file_size:
                if response_data["file_status"]:
                    res_confirm = json.loads(self.request.recv(1024).strip().decode())
                    if not res_confirm["overridden"]:
                        return True
                receive_size = 0
                m = hashlib.md5()
                f = open(file_name, "wb")
                while receive_size < file_size:
                    data = self.request.recv(4096)
                    f.write(data)
                    m.update(data)
                    receive_size += len(data)
                else:
                    result = {"server_md5": m.hexdigest()}
                    f.close()
                self.request.send(json.dumps(result).encode("utf-8"))
                result = json.loads(self.request.recv(1024).strip().decode())
                if result["status"]:
                    user_data["used_size"] += file_size
                else:
                    os.remove(file_name)
                comman_func.write_data(self.user, user_data)
        def get(self, *args, **kwargs):
            server_file = os.path.join(args[0]["file_dir"], args[0]["file_name"])
            file_info = {}
            if os.path.isfile(server_file):
                file_info["file_size"] = os.path.getsize(server_file)
            else:
                file_info["file_size"] = 0
            self.request.send(json.dumps(file_info).encode("utf-8"))
            if file_info["file_size"] > 0:
                r = self.request.recv(1024)
                f = open(server_file, "rb")
                m = hashlib.md5()
                for line in f:
                    m.update(line)
                    self.request.send(line)
                else:
                    server.md5 = m.hexdigest()
                r = self.request.recv(1024)
                self.request.send(json.dumps({"server_md5": server.md5}).encode("utf-8"))
     
        def handle(self):
            while True:
                try:
                    data = self.request.recv(1024).strip()
                    cmd_info = json.loads(data.decode())
                    action = cmd_info["action"]
                    if hasattr(self, action):
                        func = getattr(self, action)
                        func(cmd_info)
                except (ConnectionResetError, json.decoder.JSONDecodeError, TypeError) as e:
                    print("客户端断开了!", e)
                    break
    if __name__ == "__main__":
        host, port = "0.0.0.0", 8888
        server = socketserver.ThreadingTCPServer((host, port), MyTCPHandler)
        server.serve_forever()
     
     
     
    2.  comman_func.py        #方法函数
     
    # Author:Brace Li
    import os, json, platform
    base_dir = os.path.dirname(os.path.dirname(os.path.abspath("__file__")))
    def get_os_type():
        """
        get os type
        :return: W for Windows, L for Linux
        """
        ostype = platform.system()
        if ostype == 'Windows':
            return "W"
        else:
            return "L"
    def get_path(user, flag):
        """
        for get user's data and its home directory
        :param user: for user name
        :param flag: user or data, user for user info, data for home directory
        :return: return the full path of file or directory'
        """
        ostype = get_os_type()
        if ostype == "L":
            user_file = "%s/conf/user_data/%s.json" % (base_dir, user)
            user_dir = "%s/home/%s" % (base_dir, user)
        else:
            user_file = r"%sconfuser_data\%s.json" % (base_dir, user)
            user_dir = "%shome\%s" % (base_dir, user)
        if flag == "user":
            return user_file
        else:
            return user_dir
    def get_data(user_name):
        """
        get data from file
        :param user:
        :return:
        """
        file = get_path(user_name, "user")
        with open(file, "r") as fs:
            return json.load(fs)
    def write_data(user_name, data):
        file = get_path(user_name, "user")
        with open(file, "w") as fs:
            json.dump(data, fs)

    def path_combine(dir_name, file_name):
        """
        only for combine directory and file
        :param dir_name:directory name
        :param file_name:file name
        :return:
        """
        os_type = get_os_type()
        if os_type == "W":
            dir_data = r"%s\%s" % (dir_name, file_name.replace("/", "\"))
        else:
            dir_data = r"%s/%s" % (dir_name, file_name.replace("\", "/"))
        return dir_data
    def calculate_size(user_name):
        """
        calculate total fisk space size
        :param user_name:
        :return:
        """
        data_dir = get_path(user_name, "data")
        total_size = 0
        for root, dirs, files in os.walk(data_dir):
            total_size += sum([os.path.getsize(os.path.join(root, filename)) for filename in files])
        return total_size
     
    3.   brace.json  数据库文件
    {"user_name": "brace", "user_password": "123456", "max_size": 104857600, "used_size": 70701937, "status": true}
     
     
    客户端程序目录:
    ├── bin
    │   ├── ftp_client.py
    │   └── __init__.py
    ├── conf
    │   └── __init__.py
    ├── core
    │   ├── comman_func.py
    │   ├── __init__.py
    │   └── __pycache__
    │       ├── comman_func.cpython-36.pyc
    │       └── __init__.cpython-36.pyc
    ├── __init__.py
    └── logs
        └── __init__.py
     
    1.    ftp_client.py  主程序;
    # Author:Brace Li
    import socket, os, json, sys
    import hashlib
    # load parents directory into environment
    base_dir = os.path.dirname(os.path.dirname(os.path.abspath("__file__")))
    sys.path.append(base_dir)
    from core import comman_func
    class FtpClient(object):
        """
        Ftp Client
        Just for user to operate ftp by ftp client
        """
        def __init__(self):
            self.client = socket.socket()
        def cmd_help(self, *args, **kwargs):
            msg = """
            some thing for help
            ls
            pwd
            cd .. or cd path
            mkdir dirname
            rm file_name
            get file_name
            put file_name
            """
            print(msg)
        def connection(self, ip, port):
            self.client.connect((ip, port))
        def auth(self):
            err_count = 0
            while err_count < 3:
                while True:
                    user_name = comman_func.input_check("用户名称:", (), "F")
                    user_password = comman_func.input_check("用户密码:", (), "F")
                    check_code = comman_func.get_validation_code(6)
                    print("验证码:%s" %check_code)
                    user_code = comman_func.input_check("输入验证码:", (), "F")
                    if user_code != check_code:
                        comman_func.show_error("验证码不匹配!")
                        continue
                    else:
                        break
                user_info = {
                    "action": "auth",
                    "username": user_name,
                    "userpassword": user_password
                }
                self.client.send(json.dumps(user_info).encode("utf-8"))
                server_confirm = json.loads(self.client.recv(1024).decode())
                if server_confirm["confirm_status"]:
                    print(server_confirm)
                    # self.current_dir = server_confirm["home_path"].split("ftp_server")[1]
                    self.current_dir = server_confirm["home_path"]
                    self.base_dir = server_confirm["home_path"]
                    print(self.current_dir)
                    print(self.base_dir)
                    return server_confirm
                else:
                    err_count += 1
                    comman_func.show_error("账号密码错误,重新输入!")
            else:
                sys.exit("错误超过3次,系统退出.....!")
        def cmd_pwd(self, *args, **kwargs):
            """
            show current directory
            :param args:
            :param kwargs:
            :return:
            """
            print(self.current_dir.split("ftp_server")[1])
        def cmd_cd(self, *args, **kwargs):
            cmd_split = args[0].split()
            if len(cmd_split) > 1:
                if cmd_split[1] == "..":
                    if len(self.base_dir) < len(self.current_dir):
                        self.current_dir = os.path.dirname(self.current_dir)
                else:
                    cmd_info = {
                        "action": "check_dir",
                        "current_dir": self.current_dir,
                        "new_dir": cmd_split[1]
                    }
                    self.client.send(json.dumps(cmd_info).encode("utf-8"))
                    server_confirm = json.loads(self.client.recv(1024).decode())
                    if server_confirm["confirm_status"]:
                        dir_data = comman_func.path_combine(self.current_dir, cmd_split[1])
                        self.current_dir = dir_data
                    else:
                        comman_func.show_error("目录不存在.....")
        def cmd_ls(self, *args, **kwargs):
            cmd_info = {
                "action": "ls",
                "current_dir": self.current_dir
            }
            self.client.send(json.dumps(cmd_info).encode("utf-8"))
            server_confirm = json.loads(self.client.recv(1024).decode())
            for n in server_confirm:
                print(n)
        def cmd_mkdir(self,*args, **kwargs):
            cmd_split = args[0].split()
            if len(cmd_split) > 1:
                cmd_info = {
                    "action": "mkdir",
                    "current_dir": self.current_dir,
                    "new_dir": cmd_split[1]
                }
                self.client.send(json.dumps(cmd_info).encode("utf-8"))
                server_confirm = json.loads(self.client.recv(1024).decode())
                if server_confirm: pass
        def cmd_rm(self, *args, **kwargs):
            cmd_split = args[0].split()
            if len(cmd_split) > 1:
                cmd_info = {
                    "action": "rm",
                    "current_dir": self.current_dir,
                    "rm_dir": cmd_split[1]
                }
                self.client.send(json.dumps(cmd_info).encode("utf-8"))
                server_confirm = json.loads(self.client.recv(1024).decode())
                if server_confirm: pass
        def cmd_put(self, *args, **kwargs):
            cmd_split = args[0].split()
            if len(cmd_split) > 1:
                file_name = comman_func.path_combine(os.getcwd(), cmd_split[1])
                print(file_name)
                if os.path.isfile(file_name):
                    file_size = os.stat(file_name).st_size
                    file_info = {
                        "action": "put",
                        "file_dir": self.current_dir,
                        "file_name": cmd_split[1],
                        "file_size": file_size
                    }
                    print(file_info)
                    self.client.send(json.dumps(file_info).encode("utf-8"))
                    server_response = json.loads(self.client.recv(1024).decode())
                    if server_response["balance_size"] > file_size:
                        confirm_data = {"overridden": False}
                        if server_response["file_status"]:
                            confirm_flag = comman_func.input_check("文件存在,是否覆盖(Y/N):", ("Y", "N"), "F")
                            if confirm_flag == "Y": confirm_data["overridden"] = True
                            self.client.send(json.dumps(confirm_data).encode("utf-8"))
                            if confirm_flag == "N":
                                return True
                        m = hashlib.md5()
                        step = int(file_size / 80)
                        x = 0
                        total = 0
                        f = open(file_name, "rb")
                        for line in f:
                            m.update(line)
                            self.client.send(line)
                            total += len(line)
                            if len(line) < step:
                                if total > step * x:
                                    sys.stdout.write(">")
                                    sys.stdout.flush()
                                    x += 1
                            else:
                                n = int(len(line)/step)
                                for i in range(n):
                                    sys.stdout.write(">")
                                    sys.stdout.flush()
                                    x += 1
                        else:
                            client_md5 = m.hexdigest()
                            server_md5 = json.loads(self.client.recv(1024).decode())["server_md5"]
                            #print("client_md5:", client_md5)
                            #print("server_md5:", server_md5)
                            if client_md5 == server_md5:
                                result = {"status": True}
                                print(" upload pass ....!")
                            else:
                                result = {"status": False}
                                print(" upload fail ....!")
                            self.client.send(json.dumps(result).encode("utf-8"))
                    else:
                        comman_func.show_error("空间不足........")
                else:
                    comman_func.show_error("%s is not exist ....." %cmd_split[1])
        def cmd_get(self, *args, **kwargs):
            cmd_split = args[0].split()
            if len(cmd_split) > 1:
                file_info = {
                    "action": "get",
                    "file_dir": self.current_dir,
                    "file_name": cmd_split[1],
                }
                self.client.send(json.dumps(file_info).encode("utf-8"))
                server_response = json.loads(self.client.recv(1024).decode())
                if server_response["file_size"] > 0:
                    self.client.send(b"OK")
                    file_name = comman_func.path_combine(os.getcwd(), cmd_split[1])
                    f = open(file_name, "wb")
                    receive_size = 0
                    m = hashlib.md5()
                    step = int(server_response["file_size"]/80)
                    x = 0
                    while receive_size < server_response["file_size"]:
                        line = self.client.recv(4096)
                        m.update(line)
                        f.write(line)
                        receive_size += len(line)
                        if receive_size > step * x:
                            sys.stdout.write(">")
                            sys.stdout.flush()
                            x += 1
                    else:
                        self.client.send(b"OK")
                        client_md5 = m.hexdigest()
                        server_md5 = json.loads(self.client.recv(1024).decode())["server_md5"]
                        if server_md5 == client_md5:
                            print(" get %s pass" % cmd_split[1])
                        else:
                            print(" get %s fail" % cmd_split[1])
                else:
                    comman_func.show_error("文件 [ %s ] 不存在!")
     

        def interactive(self, auth_data):
            comman_func.clear_screen()
            print("*" * 80)
            print("*%s*" %" ".center(78))
            print("*%s*" % ">>>>>>> FTP System <<<<<<<".center(78))
            print("*%s*" % " ".center(78))
            temp_str = "登陆用户:%s, 磁盘空间:%s MB, 剩余空间:%s MB" % (auth_data["user_name"], auth_data["max_size"]/(1024*1024), round((auth_data["max_size"]-auth_data["used_size"])/(1024*1024), 2))
            print("*%s*" % temp_str.center(66))
            print("*%s*" % " ".center(78))
            print("*" * 80)
            while True:
                cmd_str = comman_func.input_check(">>>", (), "F")
                cmd = cmd_str.split()[0]
                if cmd == "exit":
                    sys.exit()
                elif hasattr(self, "cmd_%s" % cmd):
                    func = getattr(self, "cmd_%s" % cmd)
                    func(cmd_str)
                else:
                    self.cmd_help()
     
    ftp = FtpClient()
    #ftp.connection("127.0.0.1", 8888)
    ftp.connection("192.168.137.11", 8888)
    auth_data = ftp.auth()
    ftp.interactive(auth_data)
     
     
    2.   comman_func.py    主要方法函数:
    # Author:Brace Li
    import random, sys, platform, os
    def show_error(info):
        """
        Show error message in process
        :param info: prompt message
        :return: None, Only print error message in screen.
        """
        print("Error:%s" % info)
    def get_os_type():
        """
        get os type
        :return: W for Windows, L for Linux
        """
        ostype = platform.system()
        if ostype == 'Windows':
            return "W"
        else:
            return "L"
    def clear_screen():
        if get_os_type() == "W":
            x = os.system("cls")
        else:
            x = os.system("clear")
        return x
    def get_validation_code(n):
        """
        parameter n is for loading the length of code
        """
        check_code = ""
        for i in range(n):
            current = random.randrange(0, n)
            if i == current:
                check_code += chr(random.randint(65, 90))
            else:
                check_code += str(random.randint(0, 9))
        return check_code
    def input_check(prompt_msg, check_range, flag):
        """
        校验输入正确性, eg: input_check("select action:", (1, 2), "T")
        :param prompt_msg: 输入提示信息
        :param check_range: check range, if it is none, use "()" instead
        :param flag: number or string, T is for int number, F is anything
        :return: the value of input.
        """
        while True:
            data = input(prompt_msg).strip()
            if not data:
                show_error("input is null!")
                continue
            if flag == "T":
                if data.isdigit():
                    data = int(data)
                else:
                    show_error("data's type is invalid !")
                    continue
            if check_range:
                if data in check_range:
                    break
                else:
                    show_error("Invalid input!")
                    continue
            else:
                break
        return data
    def path_combine(dir_name, file_name):
        """
        only for combine directory and file
        :param dir_name:directory name
        :param file_name:file name
        :return:
        """
        os_type = get_os_type()
        if os_type == "W":
            dir_data = r"%s\%s" % (dir_name, file_name.replace("/", "\"))
        else:
            dir_data = r"%s/%s" % (dir_name, file_name.replace("\", "/"))
        return dir_data
     
     
  • 相关阅读:
    Gradle 3.0 + 打包android module 为aar
    Android SELinux
    Mac OS X 修改文件创建时间、修改时间 + zip 过滤临时文件
    Android 应用运行期间系统配置(系统语言、字体大小等)改变引发的问题修改
    Docker 指令
    Ubuntu-spark安装
    Highcharts 二种导出方式
    使用Storyboard 创建ViewController
    数据持久化-Plist
    模拟器常用快捷键
  • 原文地址:https://www.cnblogs.com/brace2011/p/9291687.html
Copyright © 2020-2023  润新知