開發一個支持多用戶同時在線的FTP程序


 FTP

要求: 1.用戶加密認證
2.允許同時多用戶登錄
3.每個用戶有自己的家目錄,且只能訪問自己的家目錄
4.對用戶進行磁盤配額,每個用戶的可用空間不同
5.允許用戶在ftp server上隨意切換目錄
6.允許用戶查看當前目錄下的文件
7.允許上傳和下載文件,並保證文件的一致性md5
8.文件傳輸過程中顯示進度條
9.支持文件的斷點續傳
使用:
1.啟動ftp_server.py
2.創建用戶,輸入:用戶名(默認密碼是123)
3.啟動FTP服務器
4.啟動客戶端ftp_client.py
5.輸入用戶名和密碼:alex 123 | kris 123
6.與服務器server交互:
6.1. get 1.jpg 下載圖片
6.2. put test.txt 上傳文件
6.3. ls 查詢當前目錄下的文件列表
6.4. mkdir test 創建文件夾
6.5. cd test 切換目錄
6.6. remove test 刪除文件或空文件夾

Git:https://github.com/kris-2018/task

流程圖:

    

client客戶端

download文件是儲存下載的文件;upload是上傳文件的儲存庫(download里邊可以不放東西,等待下載即可;upload里邊放你准備上傳給服務端的文件)

ftp_client.py

# -*- coding:utf-8 -*-
#Author:Kris

import os,sys,socket,struct,pickle,hashlib

class FTPClient():
    HOST = '127.0.0.1'  # 服務端的IP
    PORT = 8080  # 服務端的端口
    MAX_RECV_SIZE = 8192
    DOWNLOAD_PATH =  os.path.join(os.path.dirname(os.path.abspath(__file__)),'download')
    UPLOAD_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)),'upload')

    def __init__(self):
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connect()

    def connect(self):
        """連接服務端server"""
        try:
            self.socket.connect((self.HOST,self.PORT))
        except Exception:
            exit('\033[1;31mserver還未啟動\033[0m')

    def get_recv(self):
        """獲取server返回的數據"""
        return pickle.loads(self.socket.recv(self.MAX_RECV_SIZE))

    def auth(self):
        """用戶認證"""
        count = 0
        while count < 3:
            name = input('username>>>:').strip()
            if not name: continue
            password = input('password>>>:').strip()
            user_dic = {
                'username': name,
                'password': password
            }
            self.socket.send(pickle.dumps(user_dic)) #把用戶名和密碼發送給server
            res = struct.unpack('i',self.socket.recv(4))[0]
            if res:  #接收返回的信息,並判斷
                print('welcome'.center(20,'-'))
                user_info_dic = self.get_recv()
                self.username = user_info_dic.get('username')
                print(user_info_dic)
                return True
            else:
                print('\033[1;31m用戶名或密碼不對!\033[0m')
            count += 1

    def readfile(self):
        """讀取文件,得到文件內容的bytes型"""
        with open(self.filepath,'rb') as f:
            filedata = f.read()
        return filedata

    def getfile_md5(self):
        """對文件內容md5"""
        return hashlib.md5(self.readfile()).hexdigest()

    def progress_bar(self,num,get_size,file_size):
        """進度條顯示"""
        float_rate = get_size / file_size
        # rate = str(float_rate * 100)[:5]  # 95.85%
        rate = round(float_rate * 100,2)  # 95.85%

        if num == 1:  #1表示下載
            sys.stdout.write('\r已下載:\033[1;32m{0}%\033[0m'.format(rate))
        elif num == 2:  #2 表示上傳
            sys.stdout.write('\r已上傳:\033[1;32m{0}%\033[0m'.format(rate))
        sys.stdout.flush()

    def get(self):
        """從server下載文件到client"""
        if len(self.cmds) > 1:
            filename = self.cmds[1]
            self.filepath = os.path.join(self.DOWNLOAD_PATH, filename) #結合目錄名和文件名
            if os.path.isfile(self.filepath): #如果文件存在 支持斷點續傳
                temp_file_size = os.path.getsize(self.filepath)
                self.socket.send(struct.pack('i',temp_file_size))
                header_size = struct.unpack('i', self.socket.recv(4))[0]
                if header_size: #如果存在
                    header_dic = pickle.loads(self.socket.recv(header_size))
                    print(header_dic)
                    filename = header_dic.get('filename')
                    file_size = header_dic.get('file_size')
                    file_md5 = header_dic.get('file_md5')

                    if temp_file_size == file_size:
                        print('\033[1;32m文件已存在\033[0m')
                    else:
                        print('\033[1;33m正在進行斷點續傳...\033[0m')
                        download_filepath = os.path.join(self.DOWNLOAD_PATH, filename)
                        with open(download_filepath, 'ab') as f:
                            f.seek(temp_file_size)
                            get_size = temp_file_size
                            while get_size < file_size:
                                file_bytes = self.socket.recv(self.MAX_RECV_SIZE)
                                f.write(file_bytes)
                                get_size += len(file_bytes)
                                self.progress_bar(1, get_size, file_size)  # 1表示下載

                        if self.getfile_md5() == file_md5:  #判斷下載下來的文件MD5值和server傳過來的MD5值是否一致
                            print('\n\033[1;32m下載成功\033[0m')
                        else:
                            print('\n\033[1;32m下載文件大小與源文件大小不一致,請重新下載,將會支持斷點續傳\033[0m')
                else:
                    print('\033[1;31m該文件,之前被下載了一部分,但是server端的該文件,已被刪除,無法再次下載\033[0m')
            else:  #文件第一次下載
                self.socket.send(struct.pack('i',0))  # 0表示之前沒有下載過
                header_size = struct.unpack('i', self.socket.recv(4))[0]
                if header_size:
                    header_dic = pickle.loads(self.socket.recv(header_size))
                    print(header_dic)
                    filename = header_dic.get('filename')
                    file_size = header_dic.get('file_size')
                    file_md5 = header_dic.get('file_md5')

                    download_filepath = os.path.join(self.DOWNLOAD_PATH, filename)
                    with open(download_filepath, 'wb') as f:
                        get_size = 0
                        while get_size < file_size:
                            file_bytes = self.socket.recv(self.MAX_RECV_SIZE)
                            f.write(file_bytes)
                            get_size += len(file_bytes)
                            self.progress_bar(1, get_size, file_size)  #1表示下載
                            print('總大小:%s已下載:%s'% (file_size, get_size))
                    if self.getfile_md5() == file_md5:  #判斷下載下來的文件MD5值和server傳過來的MD5值是否一致
                        print('\n\033[1;32m恭喜您,下載成功\033[0m')
                    else:
                        print('\n\033[1;32m下載失敗,再次下載支持斷點續傳\033[0m')
                else:
                    print('\033[1;31m當前目錄下,文件不存在\033[0m')
        else:
            print('用戶沒有輸入文件名')

    def put(self):
        """往server自己的home/alice目錄下,當前工作的目錄下上傳文件"""
        if len(self.cmds) > 1:  #確保用戶輸入了文件名
            filename = self.cmds[1]
            filepath = os.path.join(self.UPLOAD_PATH, filename)
            if os.path.isfile(filepath):
                self.socket.send(struct.pack('i', 1))
                self.filepath = filepath
                filesize = os.path.getsize(self.filepath)
                header_dic = {
                    'filename': filename,
                    'file_md5': self.getfile_md5(),
                    'file_size': filesize
                }
                header_bytes = pickle.dumps(header_dic)
                self.socket.send(struct.pack('i', len(header_bytes)))
                self.socket.send(header_bytes)

                state = struct.unpack('i', self.socket.recv(4))[0]
                if state:  #已經存在了
                    has_state = struct.unpack('i', self.socket.recv(4))[0]
                    if has_state:
                        quota_state = struct.unpack('i', self.socket.recv(4))[0]
                        if quota_state:
                            has_size = struct.unpack('i', self.socket.recv(4))[0]
                            with open(self.filepath, 'rb') as f:
                                f.seek(has_size)
                                for line in f:
                                    self.socket.send(line)
                                    recv_size = struct.unpack('i', self.socket.recv(4))[0]
                                    self.progress_bar(2, recv_size, filesize)
                            success_state = struct.unpack('i', self.socket.recv(4))[0]
                            # 這里一定要判斷,因為最后一次send(line)之后等待server返回,
                            # server返回,最后一次的recv_size==file_size,但client已經跳出了循環,
                            # 所以在for外面接收的success_state其實時file_size,這種情況只針對大文件
                            if success_state == filesize:
                                success_state = struct.unpack('i', self.socket.recv(4))[0]

                            if success_state:
                                print('\n\033[1;32m恭喜您,上傳成功\033[0m')
                            else:
                                print('\n\033[1;32m上傳失敗\033[0m')
                        else: #超出了配額
                            print('\033[1;31m超出了用戶的配額\033[0m')
                    else:  # 存在的大小 和文件大小一致 不必再傳
                        print('\033[1;31m當前目錄下,文件已經存在\033[0m')
                else:  #第一次傳
                    quota_state = struct.unpack('i', self.socket.recv(4))[0]
                    if quota_state:
                        with open(self.filepath, 'rb') as f:
                            send_bytes = b''
                            for line in f:
                                self.socket.send(line)
                                send_bytes += line
                                print('總大小:%s 已上傳:%s' % (filesize, len(send_bytes)))

                                recv_size = struct.unpack('i', self.socket.recv(4))[0]
                                self.progress_bar(2, recv_size, filesize)

                        success_state = struct.unpack('i', self.socket.recv(4))[0]

                        if success_state == filesize:
                            success_state = struct.unpack('i', self.socket.recv(4))[0]

                        if success_state:
                            print('\n\033[1;32m恭喜您,上傳成功\033[0m')
                        else:
                            print('\n\033[1;32m上傳失敗\033[0m')
                    else:  # 超出了配額
                        print('\033[1;31m超出了用戶的配額\033[0m')
            else:  #文件不存在
                print('\033[1;31m文件不存在\033[0m')
                self.socket.send(struct.pack('i', 0))
        else:
            print('用戶沒有輸入文件名')

    def ls(self):
        """查詢當前工作目錄下,文件列表"""
        dir_size = struct.unpack('i', self.socket.recv(4))[0]
        recv_size = 0
        recv_bytes = b''
        while recv_size < dir_size:
            temp_bytes = self.socket.recv(self.MAX_RECV_SIZE)
            recv_bytes += temp_bytes
            recv_size += len(temp_bytes)
        print(recv_bytes.decode('gbk'))  # gbk適合windows utf-8 適合linux

    def mkdir(self):
        """增加目錄"""
        if len(self.cmds) > 1:
            res = struct.unpack('i',self.socket.recv(4))[0]
            if res:
                print('\033[1;32m在當前目錄下,增加目錄: %s 成功\033[0m'%self.cmds[1])
            else:
                print('\033[1;31m增加目錄失敗\033[0m')
        else:
            print('沒有輸入要增加的目錄名')

    def cd(self):
        """切換目錄"""
        if len(self.cmds) > 1:
            res = struct.unpack('i', self.socket.recv(4))[0]
            if res:
                print('\033[1;32m切換成功\033[0m')
            else:
                print('\033[1;31m切換失敗\033[0m')
        else:
            print('沒有輸入要切換的目錄名')

    def remove(self):
        """刪除指定的文件,或者文件夾"""
        if len(self.cmds) > 1:
            res = struct.unpack('i', self.socket.recv(4))[0]
            if res:
                print('\033[1;32m刪除成功\033[0m')
            else:
                print('\033[1;31m刪除失敗\033[0m')
        else:
            print('沒有輸入要刪除的文件')

    def interactive(self):
        """與server交互"""
        if self.auth():
            while True:
                try:
                    user_input = input('[%s]>>>:'%self.username)
                    if not user_input: continue
                    self.socket.send(user_input.encode('utf-8'))
                    self.cmds = user_input.split()
                    if hasattr(self,self.cmds[0]):
                        getattr(self,self.cmds[0])()
                    else:
                        print('請重新輸入')
                except Exception as e:  # server關閉了
                    print(e)
                    break
    def close(self):
        self.socket.close()

if __name__ == '__main__':
    ftp_client = FTPClient()
    ftp_client.interactive()
    ftp_client.close()

server服務端

bin下的文件 

  ftp_server.py

# -*- coding:utf-8 -*-
#Author:Kris
import os
import sys

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)

from core.main import Manager

if __name__ == '__main__':
    Manager().run()

conf下的文件

  accounts.ini(這個可以在執行中創建)

[alex]
password = 202cb962ac59075b964b07152d234b70
homedir = home/alex
quota = 10

[kris]
password = 202cb962ac59075b964b07152d234b70
homedir = home/kris
quota = 15

[shanshan]
password = 202cb962ac59075b964b07152d234b70
homedir = home/shanshan
quota = 20

  settings.py

# -*- coding:utf-8 -*-
#Author:Kris
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
ACCOUNTS_FILE = os.path.join(BASE_DIR,'conf','accounts.ini')

HOST = '127.0.0.1'
PORT = 8080

core下的文件

  main.py

# -*- coding:utf-8 -*-
#Author:Kris
from core.user_handle import UserHandle
from core.server import FTPServer

class Manager():
    def __init__(self):
        pass

    def start_ftp(self):
        """啟動ftp_server端"""
        server = FTPServer()
        server.run()
        server.close()

    def create_user(self):
        """創建用戶"""
        username = input('username>>>:').strip()
        UserHandle(username).add_user()

    def quit_func(self):
        quit('bye bye ...')

    def run(self):
        msg = '''\033[31;0m
        1.啟動ftp服務器
        2.創建用戶
        3.退出\033[0m\n
        '''
        msg_dic = {'1': 'start_ftp', '2': 'create_user', '3': 'quit_func'}
        while True:
            print(msg)
            num = input('num>>>:').strip()
            if num in msg_dic:
                getattr(self,msg_dic[num])()
            else:
                print('\033[1;31m請重新選擇\033[0m')

  server.py

# -*- coding:utf-8 -*-
#Author:Kris

import os,socket,struct,pickle,hashlib,subprocess
from conf import settings
from core.user_handle import UserHandle

class FTPServer():
    MAX_SOCKET_LISTEN = 5
    MAX_RECV_SIZE = 8192

    def __init__(self):
        self.socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.socket.bind((settings.HOST,settings.PORT))
        self.socket.listen(self.MAX_SOCKET_LISTEN)

    def server_accept(self):
        """等待client鏈接"""
        print('starting...')
        while True:
            self.conn,self.client_addr = self.socket.accept()
            print('客戶端地址:',self.client_addr)
            try:
                self.server_handle()
            except Exception as e:
                print(e)
                self.conn.close()
    def get_recv(self):
        """接收client發來的數據"""
        return pickle.loads(self.conn.recv(self.MAX_RECV_SIZE))

    def auth(self):
        """處理用戶的認證請求
        1.根據username讀取accounts.ini文件,password相比,判斷用戶是否存在
        2.將程序運行的目錄從bin/ftp_server.py修改到用戶home/alice,方便之后查詢 ls
        3.給client返回用戶的詳細信息
        """
        while True:
            user_dic = self.get_recv()
            username = user_dic.get('username')
            user_handle = UserHandle(username)
            user_data = user_handle.judge_user()
            # 判斷用戶是否存在 返回列表
            #如[('password','202cb962ac59075b964b07152d234b70'),('homedir','home/alex'),('quota','100')]
            if user_data:
                if user_data[0][1] == hashlib.md5(user_dic.get('password').encode('utf-8')).hexdigest():  # 密碼也相同
                    self.conn.send(struct.pack('i', 1)) #登錄成功返回 1
                    self.username = username
                    self.homedir_path = '%s\%s\%s'%(settings.BASE_DIR,'home',self.username)
                    os.chdir(self.homedir_path) #將程序運行的目錄名修改到用戶home目錄下
                    self.quota_bytes = int(user_data[2][1]) * 1024 * 1024  #將用戶配額的大小從M改到字節
                    user_info_dic = {
                        'username': username,
                        'homedir': user_data[1][1],
                        'quota': user_data[2][1]
                    }
                    self.conn.send(pickle.dumps(user_info_dic))  #用戶的詳細信息發送到客戶端
                    return True
                else:
                    self.conn.send(struct.pack('i', 0))  #登錄失敗返回 0
            else:
                self.conn.send(struct.pack('i', 0))

    def readfile(self):
        """讀取文件,得到文件內容的bytes型"""
        with open(self.filepath,'rb') as f:
            filedata = f.read()
        return filedata

    def getfile_md5(self):
        """對文件內容md5"""
        return hashlib.md5(self.readfile()).hexdigest()

    def get(self):
        """從server下載文件到client
        """
        if len(self.cmds) > 1:
            filename = self.cmds[1]
            filepath = os.path.join(os.getcwd(),filename) #os.getcwd()得到當前工作目錄
            if os.path.isfile(filepath): #判斷文件是否存在
                exist_file_size = struct.unpack('i', self.conn.recv(4))[0]
                self.filepath = filepath
                header_dic = {
                    'filename': filename,
                    'file_md5': self.getfile_md5(),
                    'file_size': os.path.getsize(self.filepath)
                }
                header_bytes = pickle.dumps(header_dic)
                if exist_file_size:  #表示之前被下載過 一部分
                    self.conn.send(struct.pack('i', len(header_bytes)))
                    self.conn.send(header_bytes)
                    if exist_file_size != os.path.getsize(self.filepath):
                        with open(self.filepath, 'rb') as f:
                            f.seek(exist_file_size)
                            for line in f:
                                self.conn.send(line)
                    else:
                        print('斷點和文件本身大小一樣')
                else:  #文件第一次下載
                    self.conn.send(struct.pack('i',len(header_bytes)))
                    self.conn.send(header_bytes)
                    with open(self.filepath,'rb') as f:
                        for line in f:
                            self.conn.send(line)
            else:
                print('當前目錄下文件不存在')
                self.conn.send(struct.pack('i',0))
        else:
            print('用戶沒有輸入文件名')

    def recursion_file(self,menu):
        """遞歸查詢用戶home/alice目錄下的所有文件,算出文件的大小"""
        res = os.listdir(menu) #指定目錄下所有的文件和和目錄名
        for i in res:
            path = '%s\%s' % (menu, i)
            if os.path.isdir(path):#判斷指定對象是否為目錄
                self.recursion_file(path)
            elif os.path.isfile(path):
                self.home_bytes_size += os.path.getsize(path)

    def current_home_size(self):
        """得到當前用戶home/alice目錄的大小,字節/M"""
        self.home_bytes_size = 0
        self.recursion_file(self.homedir_path)
        print('字節:',self.home_bytes_size)  # 單位是字節
        home_m_size = round(self.home_bytes_size / 1024 / 1024, 1)
        print('單位M:', home_m_size)  # 單位時 M

    def put(self):
        """從client上傳文件到server當前工作目錄下"""
        if len(self.cmds) > 1:
            state_size = struct.unpack('i', self.conn.recv(4))[0]
            if state_size:
                self.current_home_size()  #算出了home下已被占用的大小self.home_bytes_size
                header_bytes = self.conn.recv(struct.unpack('i', self.conn.recv(4))[0])
                header_dic = pickle.loads(header_bytes)
                print(header_dic)
                filename = header_dic.get('filename')
                file_size = header_dic.get('file_size')
                file_md5 = header_dic.get('file_md5')

                upload_filepath = os.path.join(os.getcwd(), filename)
                self.filepath = upload_filepath  # 為了全局變量讀取文件算md5時方便
                if os.path.exists(upload_filepath):  #文件已經存在
                    self.conn.send(struct.pack('i', 1))
                    has_size = os.path.getsize(upload_filepath)
                    if has_size == file_size:
                        print('文件已經存在')
                        self.conn.send(struct.pack('i', 0))
                    else:  #上次沒有傳完 接着繼續傳
                        self.conn.send(struct.pack('i', 1))
                        if self.home_bytes_size + int(file_size - has_size) > self.quota_bytes:
                            print('超出了用戶的配額')
                            self.conn.send(struct.pack('i', 0))
                        else:
                            self.conn.send(struct.pack('i', 1))
                            self.conn.send(struct.pack('i', has_size))
                            with open(upload_filepath, 'ab') as f:
                                f.seek(has_size)
                                while has_size < file_size:
                                    recv_bytes = self.conn.recv(self.MAX_RECV_SIZE)
                                    f.write(recv_bytes)
                                    has_size += len(recv_bytes)
                                    self.conn.send(struct.pack('i', has_size))  # 為了顯示 進度條

                            if self.getfile_md5() == file_md5: #判斷下載下來的文件MD5值和server傳過來的MD5值是否一致
                                print('\033[1;32m上傳成功\033[0m')
                                self.conn.send(struct.pack('i', 1))
                            else:
                                print('\033[1;32m上傳失敗\033[0m')
                                self.conn.send(struct.pack('i', 0))
                else:  #第一次上傳
                    self.conn.send(struct.pack('i', 0))
                    if self.home_bytes_size + int(file_size) > self.quota_bytes:
                        print('超出了用戶的配額')
                        self.conn.send(struct.pack('i', 0))
                    else:
                        self.conn.send(struct.pack('i', 1))
                        with open(upload_filepath, 'wb') as f:
                            recv_size = 0
                            while recv_size < file_size:
                                file_bytes = self.conn.recv(self.MAX_RECV_SIZE)
                                f.write(file_bytes)
                                recv_size += len(file_bytes)
                                self.conn.send(struct.pack('i', recv_size)) #為了進度條的顯示

                        if self.getfile_md5() == file_md5:  # 判斷下載下來的文件MD5值和server傳過來的MD5值是否一致
                            print('\033[1;32m上傳成功\033[0m')
                            self.conn.send(struct.pack('i', 1))
                        else:
                            print('\033[1;32m上傳失敗\033[0m')
                            self.conn.send(struct.pack('i', 0))
            else:
                print('待傳的文件不存在')
        else:
            print('用戶沒有輸入文件名')

    def ls(self):
        """查詢當前工作目錄下,先返回文件列表的大小,在返回查詢的結果"""
        subpro_obj = subprocess.Popen('dir', shell=True,
                                      stdout=subprocess.PIPE,
                                      stderr=subprocess.PIPE)
        stdout = subpro_obj.stdout.read()
        stderr = subpro_obj.stderr.read()
        self.conn.send(struct.pack('i', len(stdout + stderr)))
        self.conn.send(stdout)
        self.conn.send(stderr)

    def mkdir(self):
        """在當前目錄下,增加目錄"""
        if len(self.cmds) > 1:
            mkdir_path = os.path.join(os.getcwd(),self.cmds[1])
            if not os.path.exists(mkdir_path): #查看目錄名是否存在
                os.mkdir(mkdir_path)
                print('增加目錄成功')
                self.conn.send(struct.pack('i', 1)) #增加目錄成功,返回1
            else:
                print('目錄名已存在')
                self.conn.send(struct.pack('i', 0)) #失敗返回0
        else:
            print('用戶沒有輸入目錄名')

    def cd(self):
        """切換目錄"""
        if len(self.cmds) > 1:
            dir_path = os.path.join(os.getcwd(), self.cmds[1])
            if os.path.isdir(dir_path): #查看是否是目錄名
                previous_path = os.getcwd()  #拿到當前工作的目錄
                os.chdir(dir_path) #改變工作目錄到...
                target_dir = os.getcwd()
                if self.homedir_path in target_dir:  #判斷homedir_path是否在目標目錄
                    print('切換成功')
                    self.conn.send(struct.pack('i', 1)) #切換成功返回1
                else:
                    print('切換失敗')  # 切換失敗后,返回到之前的目錄下
                    os.chdir(previous_path)
                    self.conn.send(struct.pack('i', 0))
            else:
                print('要切換的目錄不在該目錄下')
                self.conn.send(struct.pack('i', 0))
        else:
            print('沒有傳入切換的目錄名')

    def remove(self):
        """刪除指定的文件,或者空文件夾"""
        if len(self.cmds) > 1:
            file_name = self.cmds[1]
            file_path = '%s\%s'%(os.getcwd(),file_name)
            if os.path.isfile(file_path):
                os.remove(file_path)
                self.conn.send(struct.pack('i', 1))
            elif os.path.isdir(file_path): #刪除空目錄
                if not len(os.listdir(file_path)):
                    os.removedirs(file_path)
                    print('刪除成功')
                    self.conn.send(struct.pack('i', 1))
                else:
                    print('文件夾非空,不能刪除')
                    self.conn.send(struct.pack('i', 0))
            else:
                print('不是文件也不是文件夾')
                self.conn.send(struct.pack('i', 0))
        else:
            print('沒有輸入要刪除的文件')

    def server_handle(self):
        """處理與用戶的交互指令"""
        if self.auth():
            print('\033[1;32m用戶登錄成功\033[0m')
            while True:
                try:  #try ...except 適合windows  client 斷開
                    user_input = self.conn.recv(self.MAX_RECV_SIZE).decode('utf-8')
                    #if not user_input: continue  # 這里適合 linux client 斷開
                    self.cmds = user_input.split()
                    if hasattr(self,self.cmds[0]):
                        getattr(self,self.cmds[0])()
                    else:
                        print('\033[1;31m請用戶重復輸入\033[0m')
                except Exception:
                    break

    def run(self):
        self.server_accept()

    def close(self):
        self.socket.close()

  user_handle.py

# -*- coding:utf-8 -*-
#Author:Kris

import configparser
import hashlib
import os

from conf import settings

class UserHandle():
    def __init__(self,username):
        self.username = username
        self.config = configparser.ConfigParser() #先生成一個對象
        self.config.read(settings.ACCOUNTS_FILE)
    @property
    def password(self):
        """生成用戶的默認密碼 123"""
        return hashlib.md5('123'.encode('utf-8')).hexdigest()
    @property
    def quota(self):
        """生成每個用戶的磁盤配額"""
        quota = input('請輸入用戶的磁盤配額大小>>>:').strip()
        if quota.isdigit():
            return quota
        else:
            exit('\033[1;31m磁盤配額須是整數\033[0m')
    def add_user(self):
        """創建用戶,存到accounts.ini"""
        if not self.config.has_section(self.username):
            print('creating username is : ', self.username)
            self.config.add_section(self.username)
            self.config.set(self.username,'password',self.password)
            self.config.set(self.username,'homedir','home/'+self.username)
            self.config.set(self.username,'quota',self.quota)
            with open(settings.ACCOUNTS_FILE,'w') as f:
                self.config.write(f)
            os.mkdir(os.path.join(settings.BASE_DIR, 'home', self.username))#創建用戶的home文件夾
            print('\033[1;32m創建用戶成功\033[0m')
        else:
            print('\033[1;31m用戶已存在\033[0m')

    def judge_user(self):
        """判斷用戶是否存在"""
        if self.config.has_section(self.username):
            return self.config.items(self.username)
        else:
            return

home下的文件

alex、kris為用戶自己的家目錄;家目錄下面可以自己創建新的目錄;家目錄儲存的文件就是服務端用來傳輸給客戶端的文件,執行下載功能;(可以自己添加文件、圖片、視頻格式等文件)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM