• [ python ] 项目一:FTP程序


    声明:

      该项目参考学习地址:

        http://www.cnblogs.com/lianzhilei/p/5869205.html , 感谢博主分享,如有侵权,立即删除。

    作业:开发一个支持多用户在线的FTP程序

    要求:

    1. 用户加密认证
    2. 允许同时多用户登录
    3. 每个用户有自己的家目录 ,且只能访问自己的家目录
    4. 对用户进行磁盘配额,每个用户的可用空间不同
    5. 允许用户在ftp server上随意切换目录
    6. 允许用户查看当前目录下文件
    7. 允许上传和下载文件,保证文件一致性
    8. 文件传输过程中显示进度条
    9. 附加功能:支持文件的断点续传

    程序:

    1、README

    # 作者介绍:
        author: hkey
    
    # 博客地址:
        
    # 功能实现:
    
        作业:开发一个支持多用户在线的FTP程序
    
        要求:
    
            用户加密认证
            允许同时多用户登录
            每个用户有自己的家目录 ,且只能访问自己的家目录
            对用户进行磁盘配额,每个用户的可用空间不同
            允许用户在ftp server上随意切换目录
            允许用户查看当前目录下文件
            允许上传和下载文件,保证文件一致性
            文件传输过程中显示进度条
            附加功能:支持文件的断点续传
            
    
    # 目录结构:
    
        FTP/
        ├── ftp_client/        # ftp客户端程序
        │   ├── ftp_client.py    # 客户端主程序
        │   ├── __init__.py
        └── ftp_server/        # ftp服务端程序
            ├── conf/    # 配置文件目录
            │   ├── __init__.py
            │   └── settings.py
            ├── database/    # 用户数据库
            │   ├── hkey.db
            │   └── xiaofei.db
            ├── ftp_server.py        
            ├── home/    # 用户家目录
            │   ├── hkey/
            │   └── xiaofei/
            ├── __init__.py
            ├── log/
            └── modules/    # 程序核心功能目录
                ├── auth_user.py
                ├── __init__.py
                └── socket_server.py
                
                
    # 功能实现:
    
        1. 初始化配置在conf下的settings.py 文件里声明,第一次运行创建用户家目录(home/)和数据文件(database/)
        2. 每个用户的磁盘配额为10M, 在conf/settings.py 中声明, 可以修改
        3. 本程序适用于windows,命令:cd / mkdir / pwd / dir / put / get
        4. 实现了get下载续传的功能:
                服务器存在文件, 客户端不存在,直接下载;
                服务器存在文件, 客户端也存在文件,比较大小, 一致则不传,不一致则追加续传;
                
                
    # 状态码:
        400 用户认证失败
        401 命令不正确
        402 文件不存在
        403 创建文件已经存在
        404 磁盘空间不够
        405 不续传
    
        200 用户认证成功
        201 命令可以执行
        202 磁盘空间够用
        203 文件具有一致性
        205 续传
    
        
    # 作者介绍:
        author: hkey
    
    # 博客地址:
        http://www.cnblogs.com/hukey/p/8909046.html
        
    # 功能实现:
    
        作业:开发一个支持多用户在线的FTP程序
    
        要求:
    
            用户加密认证
            允许同时多用户登录
            每个用户有自己的家目录 ,且只能访问自己的家目录
            对用户进行磁盘配额,每个用户的可用空间不同
            允许用户在ftp server上随意切换目录
            允许用户查看当前目录下文件
            允许上传和下载文件,保证文件一致性
            文件传输过程中显示进度条
            附加功能:支持文件的断点续传
            
    
    # 目录结构:
    
        FTP/
        ├── ftp_client/        # ftp客户端程序
        │   ├── ftp_client.py    # 客户端主程序
        │   ├── __init__.py
        └── ftp_server/        # ftp服务端程序
            ├── conf/    # 配置文件目录
            │   ├── __init__.py
            │   └── settings.py
            ├── database/    # 用户数据库
            │   ├── hkey.db
            │   └── xiaofei.db
            ├── ftp_server.py        
            ├── home/    # 用户家目录
            │   ├── hkey/
            │   └── xiaofei/
            ├── __init__.py
            ├── log/
            └── modules/    # 程序核心功能目录
                ├── auth_user.py
                ├── __init__.py
                └── socket_server.py
                
                
    # 功能实现:
    
        1. 初始化配置在conf下的settings.py 文件里声明,第一次运行创建用户家目录(home/)和数据文件(database/)
        2. 每个用户的磁盘配额为10M, 在conf/settings.py 中声明, 可以修改
        3. 本程序适用于windows,命令:cd / mkdir / pwd / dir / put / get
        4. 实现了get下载续传的功能:
                服务器存在文件, 客户端不存在,直接下载;
                服务器存在文件, 客户端也存在文件,比较大小, 一致则不传,不一致则追加续传;
                
                
    # 状态码:
        400 用户认证失败
        401 命令不正确
        402 文件不存在
        403 创建文件已经存在
        404 磁盘空间不够
        405 不续传
    
        200 用户认证成功
        201 命令可以执行
        202 磁盘空间够用
        203 文件具有一致性
        205 续传
    
        000 系统交互码
        
    README

    2、程序的结构

     3、ftp客户端程序

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import socket
    import os, json, sys
    
    class Myclient(object):
        def __init__(self, ip_port):
            self.client = socket.socket()
            self.ip_port = ip_port
    
        def connect(self):
            self.client.connect(self.ip_port)
    
        def start(self):
            self.connect()
            while True:
                username = input('输入用户名:').strip()
                password = input('输入密码:').strip()
                login_info = '%s:%s' % (username, password)
                self.client.sendall(login_info.encode())
                status_code = self.client.recv(1024).decode()
                if status_code == '400':
                    print('[%s] 用户密码错误!' % status_code)
                elif status_code == '200':
                    print('[%s] 登录成功!' % status_code)
                    self.interactive()
    
        def interactive(self):
            while True:
                command = input('-->').strip()
                if not command: continue
                command_str = command.split()[0]
                if hasattr(self, command_str):
                    func = getattr(self, command_str)
                    func(command)
    
        def get(self, command):
            self.client.sendall(command.encode())
            status_code = self.client.recv(1024).decode()
            if status_code == '201':
                filename = command.split()[1]
                print(filename)
                if os.path.isfile(filename):
                    self.client.sendall('403'.encode())
                    revice_size = os.stat(filename).st_size
                    response = self.client.recv(1024)
                    self.client.sendall(str(revice_size).encode())
                    status_code = self.client.recv(1024).decode()
                    print('-----------------')
                    if status_code == '205':
                        print('[%s] 续传' % status_code)
                        self.client.sendall('000'.encode())
                    elif status_code == '405':
                        print('[%s] 文件一致。' % status_code)
                        return
                else:
                    self.client.sendall('402'.encode())
                    revice_size = 0
    
                file_size = self.client.recv(1024).decode()
                file_size = int(file_size)
                response = self.client.sendall('000'.encode())
                with open(filename, 'ab') as file:
                    while revice_size != file_size:
                        data = self.client.recv(1024)
                        revice_size += len(data)
                        file.write(data)
                        self.__progress(revice_size, file_size, '下载中')
            else:
                print('[%s] Error!' % status_code)
    
        def put(self, command):
            if len(command.split()) > 1:
                filename = command.split()[1]
                if os.path.isfile(filename):
                    self.client.sendall(command.encode())
                    response = self.client.recv(1024)
                    file_size = os.stat(filename).st_size
                    self.client.sendall(str(file_size).encode())
                    status_code = self.client.recv(1024).decode()
                    if status_code == '202':
                        with open(filename, 'rb') as file:
                            for line in file:
                                send_size = file.tell()
                                self.client.sendall(line)
                                self.__progress(send_size, file_size, '上传中')
    
                    elif status_code == '404':
                        print('[%s] Error!' % status_code)
    
                else:
                    print('[401] Error!')
    
        def dir(self, command):
            self.__universal_method_data(command)
    
        def pwd(self, command):
            self.__universal_method_data(command)
    
        def mkdir(self, command):
            self.__universal_method_none(command)
    
        def cd(self, command):
            self.__universal_method_none(command)
    
    
        def __universal_method_none(self, command):
            self.client.sendall(command.encode())
            status_code = self.client.recv(1024).decode()
            if status_code == '201':
                self.client.sendall('000'.encode())
    
            else:
                print('[%s] Error!' % status_code)
    
        def __universal_method_data(self, command):
            self.client.sendall(command.encode())
            status_code = self.client.recv(1024).decode()
            print(status_code)
            if status_code == '201':
                self.client.sendall('000'.encode())
                result = self.client.recv(1024).decode()
                print(result)
            else:
                print('[%s] Error!' % status_code)
    
        def __progress(self, trans_size, file_size, mode):
            bar_length = 100
            percent = float(trans_size) / float(file_size)
            hashes = '=' * int(percent * bar_length)
            spaces = ' ' * int(bar_length - len(hashes))
            sys.stdout.write('
    %s:%.2fM/%.2fM %d%% [%s]' 
                             % (mode, trans_size/1048576, file_size/1048576, percent*100, hashes+spaces))
    
    if __name__ == '__main__':
        client = Myclient(('localhost', 8000))
        client.start()
    ftp_client.py

    4、ftp服务端启动程序

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import os, json
    from conf import settings
    from modules import socket_server
    def create_db():
        user_database = {}
        limit_size = settings.LIMIT_SIZE
        for k, v in settings.USER_INFO.items():
            username = k
            password = v
            user_home_path = settings.HOME_DIR + r'\%s' % username
            user_db_path = settings.DATABASE_DIR + r'\%s.db' % username
            user_database['username'] = username
            user_database['password'] = password
            user_database['limitsize'] = limit_size
            user_database['homepath'] = user_home_path
            if not os.path.isfile(user_db_path):
                with open(user_db_path, 'w') as file:
                    file.write(json.dumps(user_database))
    
    def create_home():
        for username in settings.USER_INFO:
            user_home_path = settings.HOME_DIR + r'\%s' % username
            if not os.path.isdir(user_home_path):
                os.popen('mkdir %s' % user_home_path)
    
    if __name__ == '__main__':
        create_db()
        create_home()
        server = socket_server.socketserver.ThreadingTCPServer(settings.IP_PORT, socket_server.Myserver)
        server.serve_forever()
    ftp_server.py

    5、conf配置文件

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import os, sys
    
    # 程序主目录
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.insert(0, BASE_DIR)
    
    # 数据库目录
    DATABASE_DIR = os.path.join(BASE_DIR, 'database')
    
    # 用户家目录
    HOME_DIR = os.path.join(BASE_DIR, 'home')
    
    # 用户配额
    LIMIT_SIZE = 10240000
    
    # 用户信息
    USER_INFO = {'hkey': '123456', 'xiaofei': 'abc'}
    
    # ip and port
    IP_PORT = ('localhost', 8000)
    settings.py

    6、database 用户数据库(初始化生成,生成程序 - conf/settings.py)

    {"homepath": "E:\py_code\FTP\ftp_server\home\hkey", "username": "hkey", "limitsize": 10240000, "password": "123456"}
    hkey.db
    {"homepath": "E:\py_code\FTP\ftp_server\home\xiaofei", "username": "xiaofei", "limitsize": 10240000, "password": "abc"}
    xiaofei.db

    7、modules 核心功能模块

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import json, os
    from conf import settings
    class User_operation(object):
        def authentication(self, login_info):
            username, password = login_info.split(':')
            DB_FILE = settings.DATABASE_DIR + r'\%s' % username
            if os.path.isfile(DB_FILE):
                user_database = self.cat_database(DB_FILE)
                if username == user_database['username'] and 
                    password == user_database['password']:
                    return user_database
    
        def cat_database(self, DB_FILE):
            with open(DB_FILE, 'r') as file:
                data = json.loads(file.read())
                return data
    auth_user.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import socketserver
    import os, json
    from os.path import getsize, join
    from modules import auth_user
    from conf import settings
    class Myserver(socketserver.BaseRequestHandler):
        def handle(self):
            try:
                while True:
                    login_info = self.request.recv(1024).decode()
                    print(login_info)
                    result = self.authenticat(login_info)
                    status_code = result[0]
                    self.request.sendall(status_code.encode())
                    if status_code == '400':
                        continue
                    self.user_db = result[1]
                    self.user_current_path = self.user_db['homepath']
                    self.user_home_path = self.user_db['homepath']
                    while True:
                        command = self.request.recv(1024).decode()
                        command_str = command.split()[0]
                        if hasattr(self, command_str):
                            func = getattr(self, command_str)
                            func(command)
                        else:
                            self.request.sendall('401'.encode())
    
            except ConnectionResetError as e:
                self.request.close()
                print('Error:', e)
    
    
        def authenticat(self, login_info):
            auth = auth_user.User_operation()
            result = auth.authentication(login_info)
            if result:
                return '200', result
            else:
                return '400', result
    
        def get(self, command):
            print('func: get()')
            if len(command.split()) > 1:
                filename = command.split()[1]
                user_file_path = self.user_current_path + r'\%s' % filename
                if os.path.isfile(user_file_path):
                    print(user_file_path)
                    self.request.sendall('201'.encode())
                    file_size = os.stat(user_file_path).st_size
                    status_code = self.request.recv(1024).decode()
                    if status_code == '403':
                        self.request.sendall('000'.encode())
                        has_send_data = int(self.request.recv(1024).decode())
                        if has_send_data < file_size:
                            self.request.sendall('205'.encode())
                            response = self.request.recv(1024).decode()
                        else:
                            self.request.sendall('405'.encode())
                            return
    
                    else:
                        has_send_data = 0
    
                    self.request.sendall(str(file_size).encode())
                    response = self.request.recv(1024)
                    with open(user_file_path, 'rb') as file:
                        file.seek(has_send_data)
                        self.request.sendall(file.read())
    
                else:
                    self.request.sendall('402'.encode())
            else:
                self.request.sendall('401'.encode())
    
        def put(self, command):
            filename = command.split()[1]
            file_path = self.user_current_path + r'\%s' % filename
            self.request.sendall('000'.encode())
            file_size = self.request.recv(1024).decode()
            file_size = int(file_size)
            limit_size = self.user_db['limitsize']
            used_size = self.__getdirsize(self.user_home_path)
            if limit_size >= file_size + used_size:
                self.request.sendall('202'.encode())
                revice_size = 0
                with open(file_path, 'wb') as file:
                    while revice_size != file_size:
                        data = self.request.recv(1024)
                        revice_size += len(data)
                        file.write(data)
    
            else:
                self.request.sendall('404'.encode())
    
        def dir(self, command):
            if len(command.split()) == 1:
                self.request.sendall('201'.encode())
                response = self.request.recv(1024)
                send_data = os.popen('dir %s' % self.user_current_path)
                self.request.sendall(send_data.read().encode())
            else:
                self.request.sendall('401'.encode())
    
        def pwd(self, command):
            if len(command.split()) == 1:
                self.request.sendall('201'.encode())
                response = self.request.recv(1024)
                self.request.sendall(self.user_current_path.encode())
            else:
                self.request.sendall('401'.encode())
    
        def mkdir(self, command):
            if len(command.split()) > 1:
                dir_name = command.split()[1]
                dir_path = self.user_current_path + r'\%s' % dir_name
                if not os.path.isdir(dir_path):
                    self.request.sendall('201'.encode())
                    response = self.request.recv(1024)
                    os.popen('mkdir %s' % dir_path)
                else:
                    self.request.sendall('403'.encode())
            else:
                self.request.sendall('401'.encode())
    
        def cd(self, command):
            print(command)
            if len(command.split()) > 1:
                dir_name = command.split()[1]
                dir_path = self.user_current_path + r'\%s' % dir_name
                user_home_path = settings.HOME_DIR + r'\%s' % self.user_db['username']
                if dir_name == '..' and len(self.user_current_path) > len(user_home_path):
                    self.request.sendall('201'.encode())
                    response = self.request.recv(1024)
                    self.user_current_path = os.path.dirname(self.user_current_path)
                elif os.path.isdir(dir_path):
                    self.request.sendall('201'.encode())
                    response = self.request.recv(1024)
                    if dir_name != '.' and dir_name != '..':
                        self.user_current_path += r'\%s' % dir_name
                else:
                    self.request.sendall('402'.encode())
            else:
                self.request.sendall('401'.encode())
    
        def __getdirsize(self, home_path):
            size = 0
            for root, dirs, files in os.walk(home_path):
                size += sum([getsize(join(root, name)) for name in files])
            return size
    socket_server.py

    程序运行效果图:

    get 续传功能

  • 相关阅读:
    oracle 聚合函数 LISTAGG ,将多行结果合并成一行
    oracle 数据库对于多列求最大值
    Java 简单的rpc 一
    centos7 安装php7
    win10下VM 中centos 安装共享文件
    CentOS7 cannot find a valid baseurl for repo base
    分布式事务
    利用虚拟映射文件加密大文件
    动态代理
    c++ 11 lambda表达式
  • 原文地址:https://www.cnblogs.com/hukey/p/8909046.html
Copyright © 2020-2023  润新知