• 小程序5:FTP程序


    目录

    1.FTP程序所需要的知识点

    2.FTP程序具体实现过程

      2.1 FTP程序之注册功能

      2.2 FTP程序之登录功能

      2.3 FTP程序之下载功能

    3.FTP程序源代码

    FTP程序所需要的知识点

    1.socketserver并发编程

    2.连续send,recv黏包现象:struct

    3.hashlib模块的md5加密

    4.静态方法staticmethod和类方法classmethod

    5.json序列化

    6.反射:hasattr,setattr

    7.os模块相关方法

    FTP程序具体实现过程

    FTP程序之注册功能

    1.要明确,FTP程序是要实现服务端的并发的,所以需要引入socketserver模块来实现并发

    2.写服务端下socketserver的基本语法[day31:socketserver的基本语法]

    # 服务端
    import socketserver
    
    class FTPServer(socketserver.BaseRequestHandler):
        def handle(self):
            pass
    
    myserver = socketserver.ThreadingTCPServer(("127.0.0.1",9000),FTPServer)
    myserver.serve_forever()
    
    
    # 客户端
    import socket
    sk = socket.socket()
    sk.connect(("127.0.0.1",9000))
    
    sk.close()

    3.用户需要自己输入账号和密码,所以在客户端需要写输入用户名和密码的方法(输入用户名和密码后,发送给服务端)

    4.在客户端定义auth方法,先写两个input输入用户名和密码

    5.输入完用户名密码之后,怎样将用户信息传给服务端呢?

    将用户名和密码以及操作做成一个字典,并用json序列化成字符串,并encode后,使用sk.send()发送给服务端

    这部分的具体代码如下所示:

    # 客户端
    def auth(opt):
        usr = input("username:").strip()
        pwd = input("password:").strip()
        dic = {"user":usr,"passwd":pwd,"operate":opt}
        str_dic = json.dumps(dic) # 将字典序列化成字符串
        sk.send(str_dic.encode()) # 将字符串转化成字节流并发送出去

    auth("register")

    6.服务端已经将用户名密码和操作发过去了,所以现在服务端需要接收一下,服务端的整体逻辑写在类中的handle方法

    再定义一个专门用来接收的方法myrecv,并使用handle方法去调用myrecv方法

    这部分的具体代码如下所示:

    # 服务端
    class FTPServer(socketserver.BaseRequestHandler):
        def handle(self):
            opt_dic = self.myrecv()
            print(opt_dic)
    
        def myrecv(self):
            info = self.request.recv(1024)
            opt_str = info.decode()
            opt_dic = json.loads(opt_str)
            return opt_dic

    通过以上步骤,我们实现了一収一发

    7.接收到了客户端发来的数据,我们就可以在服务端写一些关于注册的逻辑

    在服务端定义Auth类,专门用来实现注册登录,在handler方法也可以去调用类中的成员

    那么Auth类中应该写什么呢?

    1.首先在当前目录创建db文件夹,并在db问文件夹中创建userinfo.txt用来存放用户名和密码

    2.对密码使用md5加密

    8.在Auth类中定义md5方法,用来对密码进行一个加密操作

    # 服务端
    class Auth():
        def md5(usr,pwd):
            md5_obj = hashlib.md5(usr.encode())
            md5_obj.update(pwd.encode())
            return md5_obj.hexdigest()

    我们先加密一份数据存放到userinfo.txt中

    9.现在已经对每个用户名的密码加密了,但是还有一个问题需要考虑,在注册的时候,不能注册已经存在的用户名,所以需要对用户名进行判断

    10.定义register方法,并使用classmethod装饰器,当其他类调用register方法时,会自动传递类参数.

    11.拼接出一个userinfo所在文件的完整路径

    1.首先获取当前文件(server.py)所在的位置

    两种方法:

      方法一:os.getcwd()

      方法二:os.path.dirname(__file__)

    print(os.getcwd()) # F:OldBoyPythonweek6day36
    print(__file__) # F:/OldBoyPython/week6/day36/ceshi.py
    print(os.path.dirname(__file__)) # F:/OldBoyPython/week6/day36

    2.使用os.path.join进行路径拼接

    base_path = os.getcwd()
    userinfo = os.path.join(base_path,"db","userinfo.txt")
    print(userinfo) # F:OldBoyPythonweek6day36dbuserinfo.txt

    这样,我们就获取到了userinfo.txt的绝对路径了

    12.当有了userinfo.txt的绝对路径后,我们就可以开始文件操作

    在第9步,我们说到要检测用户名是否存在,现在我们就可以实现了

    当用户名存在时,返回一个状态False和一个用户名已存在信息提示

    @classmethod
    def register(cls, opt_dic):
        with open(userinfo, mode='r', encoding='utf-8') as fp:
            for line in fp:
                username = line.split(":")[0]
                if username == opt_dic["user"]:
                    return {"result": False, "info": "用户名存在了"}

    13.用户名存在的逻辑已经写完,接下来就是用户名可以使用的逻辑

    要注意:密码需要加密后再写入

    with open(userinfo, mode='a+', encoding='utf-8') as fp:
        # 账号就是字典的账号,密码使用md5加密处理后再写入文件
        strvar = "%s:%s
    " % (opt_dic["user"], cls.md5(opt_dic["user"], opt_dic["passwd"]))
        fp.write(strvar)

    如果登录成功了,返回一个状态True和一个注册成功信息提示

    到此,注册部分的逻辑就已经写完了,具体代码如下所示:

    @classmethod
    def register(cls, opt_dic):
        # 1.检测注册的用户是否存在
        with open(userinfo, mode='r', encoding='utf-8') as fp:
            for line in fp:
                username = line.split(":")[0]
                if username == opt_dic["user"]:
                    return {"result": False, "info": "用户名存在了"}
        # 2.当前用户可以注册
        with open(userinfo, mode='a+', encoding='utf-8') as fp:
            # 账号就是字典的账号,密码使用md5加密处理后再写入文件
            strvar = "%s:%s
    " % (opt_dic["user"], cls.md5(opt_dic["user"], opt_dic["passwd"]))
            fp.write(strvar)
    
        # 3.返回一个注册成功的状态
        return {"result": True, "info": "注册成功"}

    14.注册的register方法已经写完,但是现在我们需要将register方法和下面的FTPServer类建立联系,这个时候就需要使用反射来实现了

    换句话来说:就是想在FTPServer的handle方法中使用Auth中的register方法

    15.构建出反射,代码如下所示

    到目前为止,基本的代码已经实现,现进行测试,代码如下所示

    # 服务端
    import socketserver
    import json
    import hashlib
    import os
    
    
    # 找当前数据库文件所在的绝对路径
    base_path = os.getcwd()
    # F:OldBoyPythonweek6day36dbuserinfo.txt
    userinfo = os.path.join(base_path,"db","userinfo.txt")
    
    class Auth():
        @ staticmethod
        def md5(usr,pwd):
            md5_obj = hashlib.md5(usr.encode())
            md5_obj.update(pwd.encode())
            return md5_obj.hexdigest()
    
        @ classmethod
        def register(cls,opt_dic):
            # 1.检测注册的用户是否存在
            with open(userinfo,mode='r',encoding='utf-8') as fp:
                for line in fp:
                    username = line.split(":")[0]
                    if username == opt_dic["user"]:
                        return {"result":False,"info":"用户名存在了"}
            # 2.当前用户可以注册
            with open(userinfo, mode='a+', encoding='utf-8') as fp:
                # 账号就是字典的账号,密码使用md5加密处理后再写入文件
                strvar = "%s:%s
    " % (opt_dic["user"],cls.md5(opt_dic["user"],opt_dic["passwd"]))
                fp.write(strvar)
    
            # 3.返回一个注册成功的状态
            return {"result":True,"info":"注册成功"}
    
    class FTPServer(socketserver.BaseRequestHandler):
        def handle(self):
            opt_dic = self.myrecv()
            print(opt_dic)
            if hasattr(Auth,"register"):
                res = getattr(Auth,"register")(opt_dic)
                print(res)
    
    
        def myrecv(self):
            info = self.request.recv(1024)
            opt_str = info.decode()
            opt_dic = json.loads(opt_str)
            return opt_dic
    
    myserver = socketserver.ThreadingTCPServer(("127.0.0.1",9000),FTPServer)
    myserver.serve_forever()
    # 客户端
    import socket
    import json
    
    sk = socket.socket()
    sk.connect(("127.0.0.1",9000))
    
    # 处理収发数据的逻辑
    def auth(opt):
        usr = input("username:").strip()
        pwd = input("password:").strip()
        dic = {"user":usr,"passwd":pwd,"operate":opt}
        str_dic = json.dumps(dic) # 将字典序列化成字符串
        sk.send(str_dic.encode()) # 将字符串转化成字节流并发送出去
    
    auth("register")
    
    sk.close()

    运行结果如下图所示

    客户端输入用户名和密码

    服务端接收到客户端发来的数据

    并且userinfo.txt也已经写入了你刚才在客户端输入的用户名和密码

    16.在服务端我们可以看到注册成功/注册失败的信息了,现在我们想把这个信息发回给客户端,在客户端也能显示出来

    和服务端的myrecv方法一样,我们需要自定义一个接収方法mysend

    既然在服务端发数据,当然要在客户端接收数据

    好的,到此第一部分注册功能就全部完成了。让我们看一下运行结果

    所有的信息都应该是显示在客户端上的

    FTP程序之登录功能

    1.现在添加了登录功能,所以反射的时候就要动态起来。

    2.Auth类中只有注册和登录两个方法,如果用户在客户端传入其他方法,必须要给予错误的提示

    下面,我们来测试一下结果

    3.现在就可以开始写登录函数的逻辑了。。。

    登录嘛,肯定是要验证用户名和密码的,所以肯定需要从userinfo.txt中取出用户名和密码进行比对

    所以先进行文件操作,将用户名和密码取出来,在进行验证

    @ classmethod
    def login(cls,opt_dic):
        with open(userinfo,mode='r',encoding='utf-8') as fp:
            for line in fp:
                username,password = line.strip().split(":")
                if username == opt_dic["user"] and password == cls.md5(opt_dic["user"],opt_dic["passwd"]):
                    return {"result":True,"info":"登陆成功"}
    
            return {"result":False,"info":"登录失败"}

    其他的部分都不用改,定义了login函数,FTPServer就会自己识别是什么操作,并且通过反射获取到对应方法的返回值,将返回值发送给客户端,然后客户端接收后,打印出来

    运行结果如下图所示

    4.到此,登录部分的逻辑也已经完成了!!

    但是在客户端调用时,还是非常死板的

    这种调用方式非常的lowb,所以需要改进一下。。

    我们需要搞一个界面。

    5.先在客户端定义login函数和register函数,在函数里进行调用。

    6.除了登录和注册函数,还需要搞一个退出的功能

    在客户端定义myexit函数,用来实现退出的功能

    现在我们在客户端已经定义了退出函数,但是在服务端我们也要让服务端知道退出的状态。

    我们在客户端发送了一个opt_dic给服务端,然后服务端接收这个opt_dic

    到此,退出功能就已经实现完了。

    7.现在我们需要把登录,注册和退出形成一套界面

    def main():
        # 生成菜单界面
        for i,tup in enumerate(operate_lst,start=1):
            print(i,tup[0])
        
        # 输入相应序号,实现对应操作
        num = int(input("请选择您要进行的操作>>>"))
        res = operate_lst[num-1][1]()
        return res # 将对应操作的返回值返回出来
    
    while True:
        res = main() # 调用main获取到对应的返回值
        print(res)

    在客户端我们可以通过while True实现循环调用main,进而可以进行循环登录注册和退出。

    那么在服务端我们也应该是循环进行调用注册登录和退出

    所以需要在服务端也加上一个while True

    8.到此为止,登录,注册和退出的功能就都已经实现了。

    代码如下所示

    # 服务端
    import socketserver
    import json
    import hashlib
    import os
    
    # 找当前数据库文件所在的绝对路径
    base_path = os.path.dirname(__file__)
    # /mnt/hgfs/python31_gx/day36/db/userinfo.txt
    userinfo = os.path.join(base_path,"db","userinfo.txt")
    
    class Auth():
        @staticmethod
        def md5(usr,pwd):
            md5_obj = hashlib.md5(usr.encode())
            md5_obj.update(pwd.encode())
            return md5_obj.hexdigest()
    
        @classmethod
        def register(cls,opt_dic):
            # 1.检测注册的用户是否存在
            with open(userinfo,mode="r",encoding="utf-8") as fp:
                for line in fp:
                    username = line.split(":")[0]
                    if username == opt_dic["user"]:
                        return {"result":False,"info":"用户名存在了"}
                        
            # 2.当前用户可以注册
            with open(userinfo,mode="a+",encoding="utf-8") as fp:
                strvar = "%s:%s
    " % (   opt_dic["user"] , cls.md5(   opt_dic["user"],opt_dic["passwd"]   )    )
                fp.write(strvar)
                        
            """
                当用户上传的时候,给他创建一个专属文件夹,存放数据
            """
            
            # 3.返回状态
            return {"result":True,"info":"注册成功"}
            
        @classmethod
        def login(cls,opt_dic):
            with open(userinfo , mode="r" , encoding="utf-8") as fp:
                for line in fp:
                    username,password = line.strip().split(":")
                    if username == opt_dic["user"] and password == cls.md5( opt_dic["user"] , opt_dic["passwd"] ) :
                        return {"result":True,"info":"登录成功"}
                 
                return {"result":False,"info":"登录失败"}
            
        @classmethod
        def myexit(cls,opt_dic):
            return {"result":"myexit"}
            
    
    class FTPServer(socketserver.BaseRequestHandler):
        def handle(self):
            while True:
                opt_dic = self.myrecv()
                print(opt_dic) # {'user': 'wangwen', 'passwd': '111', 'operate': 'register'}
                if hasattr(Auth,opt_dic["operate"]):
                    # print(  getattr(Auth,"register")   )
                    res = getattr(Auth,opt_dic["operate"])(opt_dic) # login(opt_dic)
                    
                    # 如果接受的操作是myexit,代表退出
                    if res["result"] == "myexit":
                        return 
                    
                    # 把注册的状态发送给客户端
                    self.mysend(res)
                else:
                    dic = {"result":False,"info":"没有该操作"}
                    self.mysend(dic)
    
        # 接收方法
        def myrecv(self):
            info = self.request.recv(1024)
            opt_str = info.decode()
            opt_dic = json.loads(opt_str)
            return opt_dic        
            
        # 发送方法
        def mysend(self,send_info):
            send_info = json.dumps(send_info).encode()
            self.request.send(send_info)
    
    # 设置一个端口可以绑定多个程序
    # socketserver.TCPServer.allow_reuse_address = True
    myserver = socketserver.ThreadingTCPServer( ("127.0.0.1",9000) , FTPServer)
    myserver.serve_forever()
    # ### 客户端
    import socket
    import json
    """"""
    sk = socket.socket()
    sk.connect( ("127.0.0.1",9000) )
    
    # 处理收发数据的逻辑
    def auth(opt):
        usr = input("username: ").strip()
        pwd = input("password: ").strip()
        dic = {"user":usr,"passwd":pwd,"operate":opt}
        str_dic = json.dumps(dic)
        # 发送数据
        sk.send(str_dic.encode("utf-8"))
        
        # 接受服务端响应的数据
        file_info = sk.recv(1024).decode()
        file_dic = json.loads(file_info)
        return file_dic
        
    
    
    # 注册
    def register():
        res = auth("register")
        return res
    
    # 登录
    def login():
        res = auth("login")
        return res
        
    # 退出
    def myexit():    
        opt_dic = {"operate":"myexit"}
        sk.send(json.dumps(opt_dic).encode())
        exit("欢迎下次再来")
        
    
    # 第一套操作界面
    #                      0                  1                2
    operate_lst1 = [ ("登录",login) ,("注册",register) , ("退出",myexit) ]
    
    
    
    """
    1.登录
    2.注册
    3.退出
    
    1 ('登录', <function login at 0x7ff7cf171a60>)
    2 ('注册', <function register at 0x7ff7cf17e620>)
    3 ('退出', <function myexit at 0x7ff7cf171ae8>)
    """
    
    
    
    def main():
        for i,tup in enumerate(operate_lst1,start=1):
            print(i , tup[0])
        num = int(input("请选择执行的操作>>> ").strip()) # 1 2 3
        # 调用函数
        # print(operate_lst1[num-1]) ('退出', <function myexit at 0x7f801e34aa60>)
        # print(operate_lst1[num-1][1]) <function myexit at 0x7f801e34aa60>
        # operate_lst1[num-1][1]() myexit()
        res = operate_lst1[num-1][1]()
        return res
        
    while True:
        # 开启第一套操作界面
        res = main()
        print(res)
    
    sk.close()

    执行结果如下图所示

    FTP注册之下载功能

    1.当你登录成功后,要跳转到另一套界面,让用户选择下载上传还是退出

    所以我们需要像登录注册退出那套界面逻辑一样,再搞一个operate_lst2

    只有登录成功的时候,才能出现第二套界面。

    2.客户端现在已经发送过去了,那么对应的服务端也应该有所接收

     

    3.download我们后面再说,先把界面2的退出搞定

    同理,客户端的myexit有exit()直接终止程序,在服务端也要及时终止程序

    直接搞上一个return,连循环加函数全都退出

    到此,界面2的退出也已经搞定了,接下来就搞最复杂的download

    4.下载,先搞一下这个客户端

    在客户端定义一个download方法,定义一个字典,字典里写入操作和下载的文件名

    5.客户端定义了下载方法将字典发送过去,服务端也应该定义download下载方法来接收这个字典并进行逻辑操作

    # 服务端
    def download(self, opt_dic):
        filename = opt_dic["filename"]  # 获取用户在客户端输入的文件名
        file_abs = os.path.join(base_path, "video", filename)  # 获取到要下载视频的绝对路径
        if os.path.exists(file_abs):  # 如果文件存在
            dic = {"result": True, "info": "文件存在,可以下载"}
            self.mysend()
        else:  # 如果文件不存在
            pass

    6.如果文件存在可以下载,那么就可以执行下载的流程了

    在下载时,服务端需要将视频发送给客户端,因为视频很大,且需要分段发送,所以可能会存在黏包现象。

    所以需要引入struct模块,并改造mysend方法,以解决黏包现象

    # 服务端
    def mysend(self, send_info, sign=False):
        send_info = json.dumps(send_info).encode()
        if sign:
            # 1.发送数据的长度
            res = struct.pack("i", len(send_info))
            self.request.send(res)
        # 2.发送真实的数据
        self.request.send(send_info)
    # 客户端
    def myrecv(info_len=1024,sign=False):
        if sign:
            # 1.接受数据的长度
            info_len = sk.recv(4)
            info_len = struct.unpack("i",info_len)[0]
    
        # 2.接受真实的数据
        file_info = sk.recv(info_len).decode()
        file_dic = json.loads(file_info)
        return file_dic

    7.客户端向服务端发送下载操作和要下载的文件名,服务端接收到文件名称,返回一个可以下载的状态给客户端

    8.刚才服务端已经将文件存在,可以下载的提示信息发给客户端了,接下来服务端要发送客户端要下载的视频的文件名字和文件大小

    9.现在该发的都发了,最后一步就是发送真实的内容

    10.现在几乎是已经大功告成了,还差最后一点小瑕疵

    登录功能的第7步,我们说到,要想进行循环操作(循环选择下载上传和退出),需要在客户端和服务端加while True

    11.到此!!所有功能实现完毕

    运行结果如下图所示

    这个时候,我们去download文件夹,可以查看到下载的视频

    FTP程序源代码

    客户端

    # 客户端
    import socket
    import json
    import struct
    import os
    
    sk = socket.socket()
    sk.connect(("127.0.0.1",9000))
    
    def myrecv(info_len=1024,sign=False):
        if sign:
            info_len = sk.recv(4)
            info_len = struct.unpack("i",info_len)[0]
    
        file_info = sk.recv(info_len).decode()
        file_dic = json.loads(file_info)
        return file_dic
    
    
    # 处理収发数据的逻辑
    def auth(opt):
        usr = input("username:").strip()
        pwd = input("password:").strip()
        dic = {"user":usr,"passwd":pwd,"operate":opt}
        str_dic = json.dumps(dic) # 将字典序列化成字符串
        sk.send(str_dic.encode()) # 将字符串转化成字节流并发送出去
        return myrecv()
    
    
    def login():
        res = auth("login")
        return res
    
    def register():
        res = auth("register")
        return res
    
    def myexit():
        opt_dic = {"operate":"myexit"}
        sk.send(json.dumps(opt_dic).encode())
        exit("欢迎下次再来")
    
    def download():
        operate_dict = {
            "operate":"download",
            "filename":"ceshi123.mp4"
        }
        # 把要下载的文件名称传递给服务端
        operate_str = json.dumps(operate_dict)
        sk.send(operate_str.encode("utf-8"))
    
        # 接受服务端发过来的数据(是否可以操作)
        res = myrecv(sign=True)
        print(res)
    
        # 1.如果收到了服务端的可以下载的提示,就创建一个文件夹用来存放下载的视频
        if res["result"]:
            try:
                os.mkdir("mydownload")
            except:
                pass
        else:
            print("没有该文件")
        # 2.接受文件名字和文件大小
        dic = myrecv(sign=True)
        print(dic)
        # 3.接収真实的文件
        with open("./mydownload/" + dic["filename"],mode='wb') as fp:
            while dic["filesize"]:
                content = sk.recv(102400)
                fp.write(content)
                dic["filesize"] -= len(content)
        print("客户端下载完毕")
    
    
    
    
    
    operate_lst1 = [("注册",register),
                   ("登录",login),
                   ("退出",myexit)]
    operate_lst2 = [("下载",download),
                    ("退出",myexit)]
    
    def main(operate_lst):
        for i,tup in enumerate(operate_lst,start=1):
            print(i,tup[0])
    
        num = int(input("请选择您要进行的操作>>>"))
        res = operate_lst[num-1][1]()
        return res
    
    while True:
        res = main(operate_lst1)
        if res["result"]:
            while True:
                res = main(operate_lst2)
    
    
    sk.close()

    服务端

    # 服务端
    import socketserver
    import json
    import hashlib
    import os
    import struct
    
    
    # 找当前数据库文件所在的绝对路径
    base_path = os.getcwd()
    # F:OldBoyPythonweek6day36dbuserinfo.txt
    userinfo = os.path.join(base_path,"db","userinfo.txt")
    
    class Auth():
        @ staticmethod
        def md5(usr,pwd):
            md5_obj = hashlib.md5(usr.encode())
            md5_obj.update(pwd.encode())
            return md5_obj.hexdigest()
    
        @ classmethod
        def register(cls,opt_dic):
            # 1.检测注册的用户是否存在
            with open(userinfo,mode='r',encoding='utf-8') as fp:
                for line in fp:
                    username = line.split(":")[0]
                    if username == opt_dic["user"]:
                        return {"result":False,"info":"用户名存在了"}
            # 2.当前用户可以注册
            with open(userinfo, mode='a+', encoding='utf-8') as fp:
                # 账号就是字典的账号,密码使用md5加密处理后再写入文件
                strvar = "%s:%s
    " % (opt_dic["user"],cls.md5(opt_dic["user"],opt_dic["passwd"]))
                fp.write(strvar)
    
            # 3.返回一个注册成功的状态
            return {"result":True,"info":"注册成功"}
    
        @ classmethod
        def login(cls,opt_dic):
            with open(userinfo,mode='r',encoding='utf-8') as fp:
                for line in fp:
                    username,password = line.strip().split(":")
                    if username == opt_dic["user"] and password == cls.md5(opt_dic["user"],opt_dic["passwd"]):
                        return {"result":True,"info":"登陆成功"}
    
                return {"result":False,"info":"登录失败"}
    
        @ classmethod
        def myexit(cls,opt_dic):
            return {"result":"myexit"}
    
    class FTPServer(socketserver.BaseRequestHandler):
        def handle(self):
            while True:
                opt_dic = self.myrecv()
                print(opt_dic) # {'user': 'libolun', 'passwd': '111', 'operate': 'register'}
                if hasattr(Auth,opt_dic["operate"]):
                    res = getattr(Auth,opt_dic["operate"])(opt_dic)
                    if res["result"] == "myexit":
                        return
                    self.mysend(res)
    
                    if res["result"]: # 接受界面2数据
                        while True:
                            opt_dic = self.myrecv()
                            print(opt_dic)
    
                            if opt_dic["operate"] == "myexit":
                                return
    
                            if hasattr(self,opt_dic["operate"]):
                                getattr(self,opt_dic["operate"])(opt_dic)
    
    
    
                else:
                    dic = {"result":False,"info":"没有该操作"}
                    self.mysend(dic)
    
    
        def myrecv(self):
            info = self.request.recv(1024)
            opt_str = info.decode()
            opt_dic = json.loads(opt_str)
            return opt_dic
    
        def mysend(self,send_info,sign=False):
            send_info = json.dumps(send_info).encode()
            if sign:
                res = struct.pack("i",len(send_info))
                self.request.send(res)
            self.request.send(send_info)
    
        def download(self,opt_dic):
            filename = opt_dic["filename"] # 获取用户在客户端输入的文件名
            file_abs = os.path.join(base_path,"video",filename) # 获取到要下载视频的绝对路径
            if os.path.exists(file_abs): # 如果文件存在
                # 1.告诉客户端,文件存在,可以下载
                dic = {"result":True,"info":"文件存在,可以下载"}
                self.mysend(dic,sign=True)
    
                # 2.发送文件的名字和文件的大小
                filesize = os.path.getsize(file_abs)
                dic = {"filename":filename,"filesize":filesize}
                self.mysend(dic,sign=True)
    
                # 3.真正开始发送数据
                with open(file_abs,mode='rb') as fp:
                    while filesize:
                        content = fp.read(102400)
                        self.request.send(content)
                        filesize -= len(content)
                print("服务器下载完毕")
            else:
                dic = {"result":False,"info":"文件不存在"}
                self.mysend(dic,sign=True)
    
    
    myserver = socketserver.ThreadingTCPServer(("127.0.0.1",9000),FTPServer)
    myserver.serve_forever()
  • 相关阅读:
    查询死锁和处理死锁(SqlServer)
    日期函数(SqlServer)
    [Shell] echo/输出 中引用命令
    Github 团队协作基本流程与命令操作 图解git工作流程
    HTML 引入 CSS、JS 的三种方式
    JavaScript 字符串匹配 | JS 的正则用法 | 从后边匹配
    Sublime + Chrome 本地调试 CSS 选择器
    常用 CSS 选择器
    使用 Sublime 或其他编辑器调试 Tampermonkey 油猴脚本
    使用 chrome 扩展 Vimium 实现快捷键关闭其他标签页
  • 原文地址:https://www.cnblogs.com/libolun/p/13543225.html
Copyright © 2020-2023  润新知