python-实现tcp上传下载文件

tcp实现上传下载文件

代码存在的问题:

1、没有实现用户输入的路径的检测(太懒了),用户输入的路径不存在会导致程序中断。

2、代码的复用率不高,还可以进行简化。

3、上传文件的路径需要手动输入。

4、本来不应该设置注册功能,应该只设置登录功能。

代码放在一个文件夹内:

1、download_file : 保存用户下载的文件

2、save_up_file: 保存用户上传的文件。

3、应该还有一个文件夹,里面存放着要共享给用户的文件

4、login_file:保存用户和加密的密码,格式:用户|密码(hmac加密后)

5、server.py和client.py

服务端:

提供:登录/注册,上传文件,下载文件(指定目录下的文件)

使用的模块:

import struct  将数字转成四字节的字节码
import socketserver  实现并发访问
import hmac  密码加密
import os 
import sys 
import json 

代码的实现:主要是验证区,实现用户登录注册功能;功能区,提供上传、下载文件的功能。

服务端的代码:

​
import struct
import socketserver
import hmac
import os
import sys
import json
#用户密码加密
def md5(pwd):
    h = hmac.new(b'admin',pwd.encode('utf-8'))
    ret = h.digest()
    return ret
#读取login_file,返回字典
def read_login_file(path): #path=login_file
    dic = {}
    if os.path.exists(path):
        with open(path, 'r', encoding='utf-8') as fp:
            for line in fp:
                key, value = line.strip().split('|')
                dic[key] = value
        return dic
    else:
        return False
#将更新后的字典写到 login_file中
def write_login_file(dic,path):#path=login_file
    if os.path.exists(path):
        with open(path,'w',encoding='utf-8')as fp:
            for key in dic:
                msg = key+'|'+str(dic[key])+'\n'
                fp.write(msg)
        return True
    else:
        return False
​
#接收用户发送的用户名和密码
def recv_user(con):
    lis =[]
    user_len = con.recv(4)#接收用户的pack后的字节码
    leng = struct.unpack('i', user_len)[0]
    user = con.recv(leng).decode('utf-8')
    pwd_len = con.recv(4)
    leng = struct.unpack('i', pwd_len)[0]
    pwd = con.recv(leng).decode('utf-8')
    lis.append(user)
    lis.append(pwd)
    return lis
#用户登录验证密码
def login(user,pwd,path):#con就是accept 的con,path=login_file
    code_pwd = md5(pwd) #获取密码加密后的内容
    '''#在write_login_file是,将加密后密码转成str,这里的code_pwd是bytes类型
    所有要将code_pwd转成str类型,不然永远都不可能密码一样的。
    '''
    code_pwd=str(code_pwd)
    dic = read_login_file(path)
    # print(68,dic)
    if user in dic and dic[user]==code_pwd:
        return True #登录成功
    else:
        return False#登录失败
#用户注册函数
def register(user,pwd,path):# path=login_file
    dic = read_login_file(path)
    if user in dic:
        return False
    else:
        code_pwd = md5(pwd)
        dic[user]=code_pwd
        ret = write_login_file(dic,path)
        if ret ==True:
            return True
        else:
            return False
​
''' 添加的功能相应的函数: '''
#保存用户上传文件的文件夹(改成自己电脑的路径)
path_save = r'F:\installsotf\python file\python全栈\day30\作业\save_up_file'
#共享给客户的文件夹(改成自己电脑的路径)
PATH = r'F:\installsotf\python file\python全栈\day29'
​
'''客户端也要有相应的接收函数,在功能区中最先发送'''
#将功能列表发送给客户端
def send_function_list(con):
    msg = '1、上传文件\n2、下载文件'.encode('utf-8')
    msg_len = len(msg)
    msg_len = struct.pack('i', msg_len)
    con.send(msg_len)  # 将字符串长度发送过去。
    con.send(msg)  # 将字符串字节码发送过去。
​
#用户是上传文件,server是下载用户上传的文件。
def down_file(path,con):
     # 收到用户的选择。
    leng = con.recv(4) #接收文件名和大小组成的字典
    leng = struct.unpack('i', leng)[0]
    dic = con.recv(leng).decode('utf-8')
    dic = json.loads(dic) #获取到{‘file’:file,'size':size}
    # print(dic)
    path = os.path.join(path, dic['file'])
    size = dic['size']
    with open(path, 'wb')as fp:
        while size > 0:
            cont = con.recv(1024)
            fp.write(cont)
            size -= len(cont)
​
#对于用户是从服务端下载文件,对于服务端是传输给用户
def get_file(path):
    file=os.path.basename(path)
    size=os.path.getsize(path)
    dic = {'file':file,'size':size}
    return dic
def up_file(path,size,con):
    with open(path, 'rb')as fp:
        while size > 0:
            msg = fp.read(1024)
            con.send(msg)
            size -= len(msg)
def upto_file(path,con): #只是调用这个函数就能实现文件上传。
    # sc.send('1'.encode('utf-8'))  # 告诉服务器要进行的操作
    # path=input('输入文件的绝对路径:').strip()
    if os.path.exists(path):
        dic = get_file(path)  # 获取字典【如果将path改成输入,就可以随意上传文件了】
        size = dic['size']
        dic_byte = json.dumps(dic).encode('utf-8')  # 先转成json,再转成bytes
        dic_leng = len(dic_byte)  # 获取bytes的长度
        # print(71,dic_leng)
        leng = struct.pack('i', dic_leng)
        # print(73,leng)
        con.send(leng)  # 发送字典dic的长度
        con.send(dic_byte)  # 发送字典的内容
        # 打开要操作的文件,用rb模式
        up_file(path, size,con)  # 调用上传文件
        return True
    else:
        return False
#将可以共享给客户端的文件列表发送给客户端选择:
def send_file_list(path,con):
    file_list = os.listdir(path)
    file_path = []  # 保存可下载的文件的路径
    file_name = []  # 保存文件的名字,供用户选择
    for name in file_list:
        file = os.path.join(path, name)
        if os.path.isfile(file):
            file_path.append(file)
            file_name.append(name)
    # 将文件名的列表发送给客户端。
    name_list = json.dumps(file_name).encode('utf-8')
    leng = len(name_list)
    leng = struct.pack('i', leng)
    con.send(leng)  # 将文件名列表长度发送过去
    con.send(name_list)  # 将编码后的文件名列表发送过去
    return file_path
​
class MySocket(socketserver.BaseRequestHandler):
    def handle(self):
        con = self.request
        path ='login_file'
        while 1:
            num=con.recv(1).decode('utf-8')
            if num=='Q':#用户发送Q过来就退出循环。
                break
            lis = recv_user(con)  # 接收用户名密码,密码加密后,返回一个字典。
            user = lis[0]
            pwd = lis[1]
            # print(93,user,pwd) 查看发送用户名和密码是否正确
            fun_list = ['login','register']
            num = int(num)
            # print(96, num) 查看发送的选择是否正确,1 login,2 register
            #利用反射减少代码量
            if hasattr(sys.modules['__main__'],fun_list[num-1]):
                fun = getattr(sys.modules[__name__],fun_list[num-1])
                # print(fun.__name__)查看是哪个函数在起作用。
                ret = fun(user,pwd,path)
                if ret ==True:#只有登录成功后才能提供上传下载功能给用户
                    con.send('yes'.encode('utf-8'))
​
                    '''服务端功能添加区域: '''
                    if fun.__name__=='login':#代表用户登录成功,可以提供上传下载功能给用户使用。
                        while 1:
                            send_function_list(con)#将功能列表发送给客户端
                            choice = con.recv(1).decode('utf-8')
                            if choice == '1':
                                while 1:
                                    down_file(path_save,con)
                                    num = con.recv(4).decode('utf-8')
​
                                    if num == 'stop':  # 客户端发送0代表要终止上传文件服务。
                                        break
                            elif choice == '2':
                                # 1、发送文件列表的长度和内容
                                file_path = send_file_list(PATH,con)  # 传输文件名列表的长度和列表的内容。
                                while 1:
                                    # 2、接收文件序号的长度和内容
                                    num_len = con.recv(4)  # 接收用户要选择哪个文件的序号的长度
                                    num_len = struct.unpack('i', num_len)[0]
                                    file_index = con.recv(num_len).decode('utf-8')  # 获取到文件的序号
                                    index = int(file_index) - 1  # 索引=序号-1
                                    if index + 1 == 0:
                                        continue  # 当客户端输入的选择文件序号不在范围内时,要重新开始
                                    path = file_path[index]  # 获取待传输文件路径
                                    size = os.path.getsize(path)  # 获取文件的大小
                                    # 下面三个函数实现文件的所有传输
                                    # get_file(path)#获取文件的大小和文件名,打包成dic
                                    # up_file(path,size)#将将dic传输给client
                                    ret = upto_file(path,con)  # 将文件内容传输给client
                                    yes_not = con.recv(4).decode('utf-8')
​
                                    if yes_not == 'stop':#只有接收到用户方法的stop,才会退出下载文件的功能
                                        break
                            elif choice.upper() == 'E':
                                break
                            else:
                                pass
​
                elif ret==False:#注册失败或登录失败
                    con.send('not'.encode('utf-8'))
​
            ''' 下面这么长的代码跟上面 if hasattr() 下的代码是一样功能的。 '''
server = socketserver.ThreadingTCPServer(('127.0.0.1',9997),MySocket)
server.serve_forever()
​
​
​
​
​
​
​

客户端:

代码实现:用户登录注册功能,用户使用上传,下载功能。

import socket
import struct
import json
import os
​
sc= socket.socket()
sc.connect(('127.0.0.1',9997))
''' 验证用户名密码的函数,验证区 '''
def send_user(user,pwd):#发送用户的账户和密码
    user_len = len(user.encode('utf-8'))
    leng = struct.pack('i',user_len)
    sc.send(leng)#用户的字节码长度
    sc.send(user.encode('utf-8'))#发送用户名的字节码
​
    pwd_len = len(pwd.encode('utf-8'))
    leng = struct.pack('i', pwd_len)
    sc.send(leng)#密码的字节码长度
    sc.send(pwd.encode('utf-8'))#密码的字节码
​
​
​
''' 接收服务端的功能的函数,功能区 '''
#上传文件的路径
#上传文件的路径
path = r'F:\installsotf\python file\python全栈\day30\作业\test_up'
#下载文件的保存位置
load_path=r'F:\installsotf\python file\python全栈\day30\作业\download_file'
​
#接收服务端提供的功能列表,功能循环中第一个调用
def recv_function_list(sc):
    leng = sc.recv(4)
    leng = struct.unpack('i', leng)[0]
    cont = sc.recv(leng).decode('utf-8')
    return cont
​
#将要发送的文件名和大小写到字典中,下面三个函数,实现文件的上传
def get_file(path):
    file=os.path.basename(path)
    size=os.path.getsize(path)
    dic = {'file':file,'size':size}
    return dic
def up_file(path,size):
    with open(path, 'rb')as fp:
        while size > 0:
            msg = fp.read(1024)
            sc.send(msg)
            size -= len(msg)
def upto_file(path):
​
    # path=input('输入文件的绝对路径:').strip()
    if os.path.exists(path):
        dic = get_file(path)  # 获取字典【如果将path改成输入,就可以随意上传文件了】
        size = dic['size']
        dic_byte = json.dumps(dic).encode('utf-8')  # 先转成json,再转成bytes
        dic_leng = len(dic_byte)  # 获取bytes的长度
        leng = struct.pack('i', dic_leng)
        sc.send(leng)  # 发送字典dic的长度
        sc.send(dic_byte)  # 发送字典的内容
        # 打开要操作的文件,用rb模式
        up_file(path, size)  # 调用上传文件
        return True
    else:
        return False
​
#下面这个函数实现文件的下载:
def down_file(path):#将文件下载到哪个位
    # 收到用户的选择。
    # print(58)
    leng = sc.recv(4)  # 接收文件名和大小组成的字典
    # print(60,len(leng))
    leng = struct.unpack('i', leng)[0]
    dic = sc.recv(leng).decode('utf-8')
    dic = json.loads(dic)  # 获取到{‘file’:file,'size':size}
    # print(dic)
    # print(65)
    path = os.path.join(path, dic['file'])
    size = dic['size']
    with open(path, 'wb')as fp:
        while size > 0:
            cont = sc.recv(1024)
            fp.write(cont)
            size -= len(cont)
while 1:
    print('1、登录\n2、注册')
    chioce = input('输入的选择(q/Q退出):').strip()
    chioce_list=['登录','注册']
    if chioce=='1' or chioce=='2':
        user = input('输入用户名:').strip()
        pwd = input('输入密码:').strip()
        sc.send(chioce.encode('utf-8'))#发送选择给服务端
        send_user(user,pwd)
        ret = sc.recv(3).decode('utf-8')#yes 或者 not
        if ret =='yes':
            print(f'{chioce_list[int(chioce)-1]}成功。')
​
            '''  接收服务端提供功能的代码 :功能区'''
            if chioce=='1':#代表用户是在登录,且登录成功了,那么就可以享受服务端提供的上传下载文件功能。
​
                while 1:
                    cont = recv_function_list(sc)
                    print(cont)
                    choice = input('输入你的选择(e/E退出):').strip()
                    if choice.isdecimal():
                        sc.send(choice.encode('utf-8'))  # 告诉服务器要进行的操作
                        choice = int(choice)
                        if choice == 1:  # 上传文件
                            while 1:
                                path = input('输入文件绝对路径:')
                                path = path.replace('\\', '/')
                                ret = upto_file(path)
                                if ret:
                                    print('上传成功')
                                num = input('回车继续上传,stop退出上传:').strip()
                                if num=='goto' or 'stop':
                                    sc.send(num.encode('utf-8'))
                                    if num == 'stop':
                                        break
                                else:
                                    sc.send('none'.encode('utf-8'))
                        elif choice == 2:
                            # 1、接收文件列表的长度和内容
                            len_list = sc.recv(4)  # 接收列表的长度字节码
                            leng = struct.unpack('i', len_list)[0]  # unpacke成数字
                            leng = int(leng)
                            file_list = sc.recv(leng).decode('utf-8')
                            file_list = json.loads(file_list)
                            while 1:
                                print('-*' * 20)
                                print()
                                for i, name in enumerate(file_list, 1):
                                    print(f'{i},{name}')
                                num = input('输入你选择:').strip()
                                if num.isdecimal():
                                    num = int(num)
                                    if num > 0 and num <= len(file_list):
                                        # 2、发送文件序号的长度和内容
                                        leng = len(str(num).encode('utf-8'))
                                        leng = struct.pack('i', leng)
                                        sc.send(leng)  # 将文件序号的字节长度发送过去
                                        sc.send(str(num).encode('utf-8'))  # 将文件序号发送过去。
                                        # print('调用down_file前')#走到这里了
                                        # 3、接收文件的内容
                                        down_file(load_path)
                                        print('下载文件成功。')
                                        # print('调用down_file后')
                                        yes_not = input('回车继续下载,输入 stop 退出:').strip()
                                        if yes_not=='stop' or yes_not=='goto':
                                            sc.send(yes_not.encode('utf-8'))
                                            if yes_not == 'stop':
                                                break
                                        else:
                                            sc.send("none".encode('utf-8'))
                                    else:
                                        print('无此选择。')
                                        sc.send('0'.encode('utf-8'))
                                else:
                                    print('输入有误。')
                        else:
                            print('无此选择。')
                    elif choice.upper() == 'E':
                        sc.send('E'.encode('utf-8'))
                        break
                    else:
                        print('输入有误。')
                    '''接收服务端提供功能的代码的尾部:功能区'''
        else:
            print(f'{chioce_list[int(chioce)-1]}失败。')
    elif chioce.upper()=='Q':
        #发送Q给服务端,说明要退出。
        sc.send('Q'.encode('utf-8'))
        print('退出系统。')
        break
    else:
        print('输入有误。')
​
sc.close()
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TCP文件上传与下载的实现需要使用Socket编程。以下是一个简单的Python实现文件上传: 1. 客户端连接服务器,并发送文件名和文件大小。 2. 服务器接收到文件名和文件大小后,创建一个新的文件,并等待客户端发送文件内容。 3. 客户端读取文件内容,并将其发送到服务器。 4. 服务器接收到文件内容后,将其写入文件中。 代码实现: 客户端: ```python import socket def send_file(filename, host, port): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((host, port)) filesize = os.path.getsize(filename) s.sendall(f"{filename}:{filesize}".encode()) with open(filename, "rb") as f: while True: data = f.read(1024) if not data: break s.sendall(data) print("File sent successfully.") if __name__ == "__main__": send_file("test.txt", "localhost", 8000) ``` 服务器: ```python import socket def receive_file(host, port): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind((host, port)) s.listen() conn, addr = s.accept() with conn: print(f"Connected by {addr}") data = conn.recv(1024).decode() filename, filesize = data.split(":") filesize = int(filesize) with open(filename, "wb") as f: while True: data = conn.recv(1024) if not data: break f.write(data) print("File received successfully.") if __name__ == "__main__": receive_file("localhost", 8000) ``` 文件下载: 1. 客户端连接服务器,并发送要下载的文件名。 2. 服务器接收到文件名后,打开文件并将其内容发送给客户端。 3. 客户端接收到文件内容后,将其写入文件中。 代码实现: 客户端: ```python import socket def receive_file(filename, host, port): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((host, port)) s.sendall(filename.encode()) with open(filename, "wb") as f: while True: data = s.recv(1024) if not data: break f.write(data) print("File received successfully.") if __name__ == "__main__": receive_file("test.txt", "localhost", 8000) ``` 服务器: ```python import socket def send_file(filename, host, port): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind((host, port)) s.listen() conn, addr = s.accept() with conn: print(f"Connected by {addr}") with open(filename, "rb") as f: while True: data = f.read(1024) if not data: break conn.sendall(data) print("File sent successfully.") if __name__ == "__main__": send_file("test.txt", "localhost", 8000) ``` 以上代码只是一个简单的实现,实际应用中还需要考虑文件传输过程中的错误处理、断点续传等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值