码迷,mamicode.com
首页 > 编程语言 > 详细

[ python ] 项目一:FTP程序

时间:2018-04-22 21:48:55      阅读:189      评论:0      收藏:0      [点我收藏+]

标签:col   效果图   creat   声明   创建文件   要求   and   div   pop   

声明:

  该项目参考学习地址:

    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(\r%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 续传功能
技术分享图片

 

[ python ] 项目一:FTP程序

标签:col   效果图   creat   声明   创建文件   要求   and   div   pop   

原文地址:https://www.cnblogs.com/hukey/p/8909046.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!