zoukankan      html  css  js  c++  java
  • Python之路【第十五篇】开发FTP多线程程序

    要求:
    1.用户加密认证
    2.允许同时多用户登录
    3.每个用户有自己的家目录,且只能访问自己的家目录
    4.对用户进行磁盘配额,每个用户的可用空间不同
    5.允许用户在ftp server上随意切换目录
    6.允许用户查看当前目录下的文件
    7.允许上传和下载文件,并保证文件的一致性md5
    8.文件传输过程中显示进度条
    9.支持文件的断点续传
    使用:
    1.启动ftp_server.py
    2.创建用户,输入:用户名(默认密码是zhurui)
    3.启动FTP服务器
    4.启动客户端ftp_client.py
    5.输入用户名和密码:alex zhurui | william zhurui
    6.与服务器server交互:

        

    server服务端

    bin下的文件 

    ftp_server.py

    #_*_ coding:utf-8 _*_
    #Author :simon
    
    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(这个可以在执行中创建)

    [william]
    password = 39da56d2e7a994d38b9aaf329640fc6e
    homedir = home/william
    quota = 10
    
    [zhurui]
    password = 39da56d2e7a994d38b9aaf329640fc6e
    homedir = home/zhurui
    quota = 10
    
    [simon]
    password = 39da56d2e7a994d38b9aaf329640fc6e
    homedir = home/simon
    quota = 10

    settings.py

    #_*_ coding:utf-8 _*_
    # Author:Simon
    # Datetime:2019/8/14 11:00
    # Software:PyCharm
    
    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
    
    MAX_CONCURRENT_COUNT = 10

    core下的文件

    main.py

    # _*_ coding:utf-8 _*_
    #Author:Simon
    
    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('请输入要创建的用户>:').strip()
            UserHandle(username).add_user()
    
        def quit_func(self):
            quit('get out...')
    
        def run(self):
            msg = '''33[31;0m
            1、启动ftp服务器
            2、创建用户
            3、退出33[0m
    
            '''
            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('33[1;31m请重新选择33[0m')

    server.py

    #-*- coding:utf-8 -*-
    # Author:Simon
    # Datetime:2019/8/13 21:02
    # Software:PyCharm
    
    import os
    import socket
    import struct
    import pickle
    import hashlib
    import subprocess
    import queue
    from conf import settings
    # from core.user_handle import UserHandle
    from core.user_handle import UserHandle
    
    from threading import Thread, Lock
    
    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)
    
            self.q = queue.Queue(settings.MAX_CONCURRENT_COUNT) # 可以配置最大并发数
    
        def server_accept(self):
            '''等待client连接'''
            print('starting...')
            while True:
                self.conn,self.client_addr = self.socket.accept()
                print('客户端地址:', self.client_addr)
    
                #pool.submit(self.get_recv, self.conn)
            #self.server_accept.close()
                try:
                    # t = Thread(target=self.server_handle, args=(self.conn, )) #报这个错(TypeError: server_handle() takes 1 positional argument but 2 were given)
                    t = Thread(target=self.server_handle(), args=(self.conn, ))
                    self.q.put(t)
                    t.start()
                except Exception as e:
                    print(e)
                    self.conn.close()
                    self.q.get()
    
        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))  #登录成功返回
                        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))
                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('33[1;32m上传成功33[0m')
                                    self.conn.send(struct.pack('i', 1))
                                else:
                                    print('33[1;32m上传失败33[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('33[1;32m超出了用户的配额33[0m')
                            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('33[1;32m上传成功33[0m')
                                self.conn.send(struct.pack('i', 1))
                            else:
                                print('33[1;32m上传失败33[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('33[1;32m用户登陆成功33[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('33[1;31请用户重复输入33[0m')
                    except Exception:
                        break
    
        def run(self):
            self.server_accept()
    
        def  close(self):
            self.socket.close()
    
    #if __name__ == '__main__':
        #pool = ThreadPoolExecutor(10)

    user_handle.py

    #_*_ coding:utf-8 _*_
    # Author:Simon
    # Datetime:2019/8/14 10:26
    # Software:PyCharm
    
    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):
            '''生成用户的默认密码 zhurui'''
            return hashlib.md5('zhurui'.encode('utf-8')).hexdigest()
        @property
    
        def quota(self):
            '''生成每个用户的磁盘配额'''
            quota = input('请输入用户的磁盘配额大小>>>:').strip()
            if quota.isdigit():
                return quota
            else:
                exit('33[1;31m磁盘配额必须是整数33[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('33[1;32m创建用户成功33[0m')
            else:
                print('33[1;32m用户已存在33[0m')
    
    
        def judge_user(self):
            '''判断用户是否存在'''
            if self.config.has_section(self.username):
                return self.config.items(self.username)
            else:
                return

    client客户端

    download文件是储存下载的文件;upload是上传文件的储存库(download里边可以不放东西,等待下载即可;upload里边放你准备上传给服务端的文件)

    ftp_client.py

    #_*_ coding:utf-8 _*_
    # Author:Simon
    # Datetime:2019/8/14 11:12
    # Software:PyCharm
    
    import os
    import sys
    import socket
    import struct
    import pickle
    import 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('33[1;31mserver还未启动33[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('请输入用户名>>:').strip()
                if not name: continue
                password = input('请输入密码>>:').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('33[1;31m用户名或者密码不对!33[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('
    已下载:33[1;32m{0}%33[0m'.format(rate))
            elif num == 2:   #2 表示上传
                sys.stdout.write('
    已上传:33[1;32m{0}%33[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('33[1;32m文件已经存在33[0m')
                        else:
                            print('33[1;33m正在进行断点续传....33[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('
    33[1;32m下载成功33[0m')
                            else:
                                print('
    33[1;32m下载文件大小与源文件大小不一致,请重新下载,将会支持断点续传033[0m')
                    else:
                        print('
    33[1;31m该文件,之前被下载了一部分,但是server端的该文件,已被删除,无法再次下载33[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('
    33[1;32m恭喜你,下载成功33[0m')
                        else:
                            print('
    33[1;32m下载失败,再次下载支持断点续传33[0m')
                    else:
                        print('
    33[1;32m当前目录下,文件不存在33[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('
    33[1;32m恭喜您,上传成功33[0m')
                                else:
                                    print('
    33[1;32m上传失败33[0m')
                            else:  #超出了配额
                                print('33[1;31m超出了用户的配额33[0m')
                        else:  #存在的大小和文件大小一致,不必再传
                            print('33[1;31m当前目录下,文件已经存在33[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)
                            succes_state = struct.unpack('i',self.socket.recv(4))[0]
    
                            if succes_state == filesize:
                                succes_state = struct.unpack('i',self.socket.recv(4))[0]
                            if succes_state:
                                print('
    33[1;32m恭喜您,上传成功33[0m')
                            else:
                                print('
    33[1;32m上传失败33[0m')
                        else:   #超出了配额
                            print('33[1;31m超出了用户的配额33[0m')
                else:  #文件不存在
                    print('33[1;31m文件不存在33[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('33[1;32m在当前目录下,增加目录: %s 成功33[0m'% self.cmds[1])
                else:
                    print('33[1;31m增加目录失败33[0m')
            else:
                print('没有输入要增加的目录名')
    
        def cd(self):
            '''切换目录'''
            if len(self.cmds) > 1:
                res = struct.unpack('i', self.socket.recv(4))[0]
                if res:
                    print('33[1;32m切换成功33[0m')
                else:
                    print('33[1;32m切换失败33[0m')
            else:
                print('没有输入要切换的目录名')
    
        def remove(self):
            '''删除指定的文件,或者文件夹'''
            if len(self.cmds) > 1:
                res = struct.unpack('i', self.socket.recv(4))[0]
                if res:
                    print('33[1;32m删除成功33[0m')
                else:
                    print('33[1;31m删除失败33[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()
  • 相关阅读:
    CSS: 三栏布局
    CSS: 实现两栏布局,左边固定,右边自适应的4种方法
    css清除浮动
    浏览器解析时间线
    @Valid解决无法校验List问题
    Docker+Jenkins+Git+Maven实现Springboot项目自动化部署
    Git安装(CentOS)
    Jenkins安装
    Docker安装(Centos)
    Maven安装(Linux)
  • 原文地址:https://www.cnblogs.com/hackerer/p/11355665.html
Copyright © 2011-2022 走看看