小程序支持多用户在线的FTP程序

Posted q1ang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小程序支持多用户在线的FTP程序相关的知识,希望对你有一定的参考价值。

功能:
1.用户加密认证;

2.允许同时多用户登陆;

3.每个用户有自己的家目录,并且只能访问在自己的家目录;

4.对用户进行磁盘配额,每个用户的可用空间不同;

5.允许用户在ftp server上随意切换目录;

6.允许用户查看当前目录上下文;

7.允许用户上传和下载文件,保证文件的一致性

8.文件传输过程中显示进度条;

 

客户端:

# Author:q1.ang

import socket,os,json
import hashlib
import sys

class FtpClient(object):
    def __init__(self):
        self.client = socket.socket()
        self.account=\'\'
        self.path=\'\'

    def help(self):
        \'\'\'
        帮助
        :return: none
        \'\'\'
        msg=\\
        \'\'\'help:
        dir
        cd ../..
        new ..
        del ..
        re ..
        put ..
        get ..\'\'\'
        print(msg)

    def connect(self,ip,port):
        \'\'\'
        连接
        :param ip:
        :param port:
        :return:
        \'\'\'
        self.client.connect((ip,port))

    def authenticate(self):
        \'\'\'
        用户登陆认证
        :return:auth真假
        \'\'\'
        auth=False
        try_count=0
        while try_count<3:
            self.account=input(\'Account:\').strip()
            password=input(\'Password:\').strip()
            #获取账号密码的MD5值
            m=hashlib.md5()
            m.update((self.account+password).encode(\'utf-8\'))
            #发送acc+MD5
            self.client.send((self.account+\' \'+m.hexdigest()).encode(\'utf-8\'))
            #接收服务器的认证结果
            auth_recv=json.loads(self.client.recv(1024).decode())
            print(auth_recv[\'log res\'])
            if auth_recv[\'log res\']==\'login success\':
                auth=True
                print(\'desk size:\',auth_recv[\'desk size\'])
                return auth
            try_count+=1
            if try_count==3:
                print(\'you have try 3 times...exit\')
        else:
            return auth

    def interactive(self):
        \'\'\'
        交互模块,调用各个功能,在子功能中与服务器实现收发
        :return:none
        \'\'\'
        if self.authenticate(): #认证
            while True:
                cmd = input(\'>>>\').strip()
                if len(cmd)==0:continue
                cmd_str=cmd.split()[0]
                if hasattr(self,\'cmd_%s\'%cmd_str):
                    func=getattr(self,\'cmd_%s\'%cmd_str)
                    func(cmd)
                else:
                    self.help()

    def cmd_dir(self,*args):
        \'\'\'
        查看当面目录下文件
        :param args: dir没使用
        :return: none
        \'\'\'
        msg_dic = {
            \'action\':\'dir\'
        }
        self.client.send(json.dumps(msg_dic).encode(\'utf-8\'))
        print(self.account,\'list:\')
        server_respone = self.client.recv(1024).decode()
        for i in json.loads(server_respone):
            print(i)

    def cmd_cd(self,*args):
        \'\'\'
        切换目录
        :param args: cd 目录名
        :return: none
        \'\'\'
        cmd_split=args[0].split()
        if len(cmd_split)>1:
            if cmd_split[1][0:len(self.account)]==self.account:
                self.path = \'\\\\\'+cmd_split[1][len(self.account)+1:]
            else:
                self.path += \'\\\\\'+cmd_split[1]
            msg_dic = {
                \'action\': \'cd\',
                \'path\':self.path
            }
            print(msg_dic)
            self.client.send(json.dumps(msg_dic).encode(\'utf-8\'))
            auth_path=self.client.recv(1024).decode()

            if  auth_path== \'True\':
                print((self.account+\':\'+self.path).replace(\'\\\\\\\\\',\'\\\\\'))
            else:
                print(\'找不到指定的路径\')
        else:
            print(\'you not write the folder name...\')

    def cmd_new(self,*args):
        \'\'\'
        新建目录
        :param args: cmd
        :return: new 目录名
        \'\'\'
        cmd_split=args[0].split()
        if len(cmd_split)>1:
            foldername=cmd_split[1]
            msg_dic={
                \'action\':\'new\',
                \'foldername\':foldername
            }
            self.client.send(json.dumps(msg_dic).encode(\'utf-8\'))
            recv=self.client.recv(1024).decode()
            print(recv)
        else:
            print(\'no name of the new file...\')

    def cmd_del(self,*args):
        \'\'\'
        删除目录或文件
        :param args:del 文件名
        :return: none
        \'\'\'
        cmd_split=args[0].split()
        if len(cmd_split)>1:
            msg_dic={
                \'action\':\'del\',
                \'name\':cmd_split[1]
            }
            self.client.send(json.dumps(msg_dic).encode(\'utf-8\'))
            recv=self.client.recv(1024).decode()
            print(recv)

        else:
            print(\'no name of the file...\')

    def cmd_re(self,*args):
        \'\'\'
        重命名
        :param args: re 文件名
        :return: none
        \'\'\'
        cmd_split=args[0].split()
        if len(cmd_split)>1:
            new_name=input(\'new name:\').strip()
            msg_dic={
                \'action\':\'re\',
                \'name\':cmd_split[1],
                \'new name\':new_name
            }
            self.client.send(json.dumps(msg_dic).encode(\'utf-8\'))
            recv=self.client.recv(1024).decode()
            print(recv)
        else:
            print(\'the name of file or folder has not write..\')

    def cmd_put(self,*args):
        \'\'\'
        上传文件
        :param args: put 文件名
        :return: none
        \'\'\'
        cmd_split=args[0].split()
        if len(cmd_split)>1:
            filename=cmd_split[1]
            if os.path.isfile(filename):
                filesize=os.stat(filename).st_size
                msg_dic = {
                    \'action\': \'put\',
                    \'filename\': filename,
                    \'filesize\': filesize,
                    \'override\': True
                }
                self.client.send(json.dumps(msg_dic).encode(\'utf-8\'))

                server_respone=self.client.recv(1024).decode()
                if server_respone==\'out of memory\':
                    print(\'the mamory of your desk is not enough to save this file[%s(%d Mb)]\' % (filename, filesize / 1048576))
                else:
                    l=0
                    sys.stdout.write(\'0%\')
                    sys.stdout.flush()
                    with open(filename,\'rb\') as f:
                    # f=open(filename,\'rb\')
                        for line in f:
                            self.client.send(line)
                            l+=len(line)
                            if l>(filesize/20):
                                sys.stdout.write(\'>\')
                                sys.stdout.flush()
                                l-=(filesize/20)
                        else:
                            print(\'100%\\nfile upload success...\')
                            save_sigle=self.client.recv(1024).decode()
                            print(save_sigle)
            else:
                print(filename,\'is not exit\')

    def cmd_get(self,*args):
        \'\'\'
        下载文件
        :param args: get 文件名
        :return: none
        \'\'\'
        print(\'get\')
        cmd_split=args[0].split()
        if len(cmd_split)>1:
            filename=cmd_split[1]
            msg_dic={
                \'action\': \'get\',
                \'filename\':filename
            }
            self.client.send(json.dumps(msg_dic).encode(\'utf-8\'))
            auth_recv=json.loads(self.client.recv(1024).decode())
            if auth_recv[\'file acc\']==\'file exist\':
                filesize=auth_recv[\'filesize\']
                revc_size=0
                l=0
                sys.stdout.write(\'0%\')
                sys.stdout.flush()
                self.client.send(b\'200\')    #防粘包
                if os.path.isfile(filename):
                    filename=filename+\'.new\'
                with open(filename,\'wb\')as f:
                    while revc_size<filesize:
                        recv=self.client.recv(1024)
                        f.write(recv)
                        revc_size+=len(recv)
                        l+=len(recv)
                        if l>(filesize/20):
                            sys.stdout.write(\'>\')
                            sys.stdout.flush()
                            l-=(filesize/20)
                    else:
                        print(\'100%\\nreceive done\')
            else:
                print(auth_recv[\'file acc\'])
        else:
            print(\'error:not write filename...\')

ftp=FtpClient()
ftp.connect(\'localhost\',6666)
ftp.interactive()

服务器端:

 

# Author:q1.ang
import socketserver,json,os
import hashlib

class MyTCPHandler(socketserver.BaseRequestHandler):
    acc_path = \'\'
    account =\'\'
    available_size=0
    data_dic={}
    def authenticate(self):
        \'\'\'
        用户认证
        :return: auth真假
        \'\'\'
        auth=False
        try_count = 0
        while try_count < 3:
            #接收客户端的账号密码
            account,recv_md5=self.request.recv(1024).decode().split()
            print(account,recv_md5)
            BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
            file_path=\'%s\\conf\\\\accounts\\%s\'%(BASE_DIR,account)
            if os.path.isfile(file_path):
                with open(file_path,\'r\') as f:
                    # acc_data=json.load(f)
                    self.data_dic=json.load(f)
                    m=hashlib.md5()
                    m.update((self.data_dic[\'account\']+self.data_dic[\'password\']).encode(\'utf-8\'))
                    print(m.hexdigest())
                    if m.hexdigest()==recv_md5:
                        print(\'login success\')
                        self.account=account
                        used_size = int(self.data_dic[\'desk size\'].split(\'/\')[0])
                        all_size = int(self.data_dic[\'desk size\'].split(\'/\')[1])
                        self.available_size = all_size - used_size

                        print(\'available_size\',self.available_size)

                        msg_dic = {
                            \'log res\': \'login success\',
                            \'desk size\': \'%dMb/%dMb\'%(used_size/1048576,all_size/1048576)
                        }
                        auth=True
                        try_count=3
                    else:
                        print(\'account or password error\')
                        msg_dic={
                            \'log res\':\'account or password error\',
                        }
            else:
                print(\'the account dose not exist..\')
                msg_dic = {
                    \'log res\': \'the account dose not exist..\',
                }
            try_count+=1
            self.request.send(json.dumps(msg_dic).encode(\'utf-8\'))

        return auth

    def cmd_dir(self,*args):
        \'\'\'
        查看目录下文件
        :param args: cmd_dic
        :return: none
        \'\'\'
        print(self.acc_path)
        print(os.listdir(self.acc_path))
        self.request.send(json.dumps(os.listdir(self.acc_path)).encode(\'utf-8\'))

    def cmd_cd(self,*args):
        \'\'\'
        切换目录
        :param args: cmd_dic
        :return: none
        \'\'\'
        BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        self.acc_path = \'%s\\\\conf\\\\data\\\\%s\'%(BASE_DIR,self.account)
        auth_path=\'False_path\' #不能发空字节
        path=self.acc_path+args[0][\'path\']
        print(\'地址:\',path)
        if os.path.exists(path):
            self.acc_path=path
            auth_path=\'True\'
            print(\'path\',self.acc_path)
        else:
            print(\'找不到指定的路径\')
        self.request.send(auth_path.encode(\'utf-8\'))

    def cmd_new(self,*args):
        \'\'\'
        在当前目录下新建目录
        :param args: cmd_dic
        :return: none
        \'\'\'
        path=self.acc_path+\'\\\\\'+args[0][\'foldername\']
        if os.path.exists(path):
            print(\'folder is exist..\')
            msg=\'folder is exist..\'
        else:
            os.mkdir(path)
            print(\'创建成功\')
            msg = \'folder create success..\'
        self.request.send(msg.encode(\'utf-8\'))

    def cmd_re(self,*args):
        \'\'\'
        在当前目录下重命名文件
        :param args: cmd_dic
        :return: none
        \'\'\'
        name = self.acc_path + \'\\\\\' + args[0][\'name\']
        new_name=self.acc_path + \'\\\\\' + args[0][\'new name\']
        try:
            os.rename(name,new_name)
            msg=\'rename success\'
        except FileNotFoundError as e:
            msg=\'the folder is not exist...\'
            print(e)
        self.request.send(msg.encode(\'utf-8\'))

    def cmd_del(self,*args):
        \'\'\'
        在当前目录下删除目录(空)或文件
        :param args: cmd_dic
        :return: none
        \'\'\'
        path = self.acc_path + \'\\\\\' + args[0][\'name\']
        if os.path.isfile(path):
            os.remove(path)
            msg=\'del success\'
        elif os.path.exists(path):
            try:
                os.rmdir(path)
                msg = \'del success\'
            except OSError as e:
                print(e)
                msg=\'the folder is not empty...\'
        else:
            msg=\'file or folder is not exist...\'
        self.request.send(msg.encode(\'utf-8\'))

    def cmd_put(self,*args):
        \'\'\'
        接收客户端文件
        :return:
        \'\'\'
        cmd_dic=args[0]
        filename=cmd_dic[\'filename\']
        filesize=cmd_dic[\'filesize\']

        if filesize>self.available_size:
            self.request.send(\'out of memory\'.encode(\'utf-8\'))
        else:
            file_path=\'%s\\\\%s\'%(self.acc_path,filename)
            if os.path.isfile(file_path):
                f=open(file_path+\'.new\',\'wb\')
            else:
                f = open(file_path, \'wb\')
            self.request.send(b\'200 ok\') #防粘包
            recv_size=0
            while recv_size< filesize:
                data=self.request.recv(1024)
                f.write(data)
                recv_size+=len(data)
            else:
                f.close()
                print(\'file [%s] has uploaded\'%filename)
                self.available_size -= filesize
                print(\'available_size2\',self.available_size)
                self.request.send((\'server save success\\navailable size:\'
                                   \'%d Mb\'%(self.available_size/1048576)).encode(\'utf-8\'))
                #更新用户数据
                used_size=self.data_dic[\'desk size\'].split(\'/\')[0]
                all_size=self.data_dic[\'desk size\'].split(\'/\')[1]
                print(\'filesize\',filesize)
                print(\'used_size\',used_size)
                print(\'all_size\',all_size)
                self.data_dic[\'desk size\']=\'%d/%s\'%(int(used_size)+filesize,all_size)

                BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
                file_path = \'%s\\conf\\\\accounts\\%s\' % (BASE_DIR, self.account)
                print(file_path)
                with open(file_path,\'w\') as f:
                    json.dump(self.data_dic,f)

    def cmd_get(self,*args):
        \'\'\'
        发送给客户端文件
        :param args: cmd_dic
        :return: none
        \'\'\'
        cmd_dic=args[0]
        filename=cmd_dic[\'filename\']
        file_path=\'%s\\\\%s\'%(self.acc_path,filename)
        if os.path.isfile(file_path):
            filesize=os.stat(file_path).st_size
            msg_dic={
                \'file acc\':\'file exist\',
                \'filesize\':filesize
            }
            self.request.send(json.dumps(msg_dic).encode(\'utf-8\'))
            self.request.recv(1024) #防粘包
            with open(file_path,\'rb\')as f:
                for line in f:
                    self.request.send(line)
                else:
                    print(\'file send done\')

        else:
            msg_dic = {\'file acc\': \'file[%s] is not exist...\'%filename}
            self.request.send(json.loads(msg_dic).encode(\'utf-8\'))

    def handle(self):
        \'\'\'
        重构socketserver的handle(),用来主要的交互
        :return: 
        \'\'\'
        print(\'以上是关于小程序支持多用户在线的FTP程序的主要内容,如果未能解决你的问题,请参考以下文章

Python开发程序:支持多用户在线的FTP程序

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

python之FTP程序(支持多用户在线)

python 多用户在线的FTP程序

python 开发一个支持多用户在线的FTP

FTP程序