python学习笔记 day32 实现网盘上传下载功能

1. 作业需求

借助socket模块实现server端和client端的交互,拟实现网盘上传下载的功能:

上传: client端发送请求,把本地的文件上传给server端,server端负责接收,然后server端的一个文件中写入client端上传的文件内容;

下载: client端发送请求,想要下载server端某文件,server端接收请求后,给客户端发送该文件的内容(按字节读取文件内容,然后边读边发送给客户端)

之前自己试着写了一个:

1. 版本一(不完善--bymyself)

# server.py
import socket
import pickle
sk=socket.socket()
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sk.bind(("127.0.0.1",8080))
sk.listen()
conn,addr=sk.accept()

while True:
    head=conn.recv(1024)
    head=pickle.loads(head)
    file_name=head["filename"]
    file_size=head["filesize"]
    file_type=head["filetype"]
    file_path=head["filepath"]
    file_path=file_path+'\\'+file_name+'.'+file_type
    with open(file_path, 'wb') as f:
        while file_size>0:
            content=conn.recv(1024)
            f.write(content)
            file_size-=1024
conn.close()
sk.close()
View Code
# client.py
import socket
import pickle
sk=socket.socket()
sk.connect(("127.0.0.1",8080))
while True:
    head={"filename":"test","filesize":2048,"filetype":"txt",'filepath':"E:\pyhtonworkspace\py3-pratice\Pycharm_workspace\python_fullstack\week8\day07"}
    file_size = head["filesize"]
    head=pickle.dumps(head)
    sk.send(head)
    with open("xixi","rb") as f:
        while file_size>0:
            content=f.read(1024)
            sk.send(content)
            file_size-=1024
sk.close()
View Code

也可以实现上传功能(其实就是client端发送一个请求,想把本地的某一个文件上传给server端),server端可以接收这个文件,然后写入,但是有一点点问题,到文件的最后 会多些一点乱七八糟的东西,这个问题没有解决

2. 版本二(Eva-J)

# server.py
import socket
import pickle
import struct
sk=socket.socket()
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)  # 防止重启服务器发生错误
sk.bind(("127.0.0.1",8080))  # server端绑定IP地址和端口号
sk.listen()
conn,addr=sk.accept()
buffer=1024   # 读取文件的字节大小是1024
head_len_bytes=conn.recv(4)  # 因为client端需要发送head报头的长度 int类型借助struct模板转为byets类型发送,占有固定的长度四个字节
head_len=struct.unpack("i",head_len_bytes)[0]  # 把报头head的字节长度 借助struct模块转为的bytes 再使用struct的unpack转为int的整数,代表head报头的字节长度
head_bytes=conn.recv(head_len)   # 字节类型,client端把报头head使用pickle的dumps成bytes类型,直接发送,server直接接收head的bytes类型
head=pickle.loads(head_bytes)  # 把bytes类型的报头head使用pickle反序列化为原来的字典类型
filesize=head["filesize"]  # 得到client端需要上传的文件的长度
filename=head["filename"]+"."+head["filetype"]  # 得到client端需要上传的文件的文件名
with open(filename,"wb") as f:  # 由于对于一些视频,音频等文件是无法按行读的,所以需要使用按照直接读,所以文件的打开方式都是rb 或者wb这种以二进制的方式进行的
    while filesize>=0:  # 当还有需要读取的字节数,就不断地按照特定长度的字节读取文件内容,然后写到server端的同名文件中
        if filesize>=buffer:  # 剩余的需要读取的文件字节数大于buffer时  每次就按照buffer字节来读
            content=conn.recv(buffer)
            f.write(content)
            filesize-=buffer
        else:
            content=conn.recv(filesize)
            f.write(content)
            filesize-=buffer
conn.close()
sk.close()
# client.py
import socket
import pickle
import struct
import os
sk=socket.socket()
sk.connect(("127.0.0.1",8080))
buffer=1024  # 设置文件读取的字节数
head={"filename":"2018ASID格式要求","filetype":"docx","filepath":r"F:\厦大课程-研二\研二上学期\2018ASID\格式要求","filesize":None}  # 定制报头信息
filepath=os.path.join(head["filepath"],(head["filename"]+"."+head["filetype"]))   # 拼接路径,其实就是所要上传的文件路径
filesize=os.path.getsize(filepath)  # 得到所要上传的文件的字节数大小
head["filesize"]=filesize
head_bytes=pickle.dumps(head)   # head想要从client端传到server端,网络传输必须序列化(pickle的结果时bytes,也可以使用json 序列化的结果是str)
head_bytes_len=len(head_bytes)  # head报头字节数的大小,因为网络传输都是字节数,必须要告诉对方需要接收多少字节才能准确接收到该报头信息
head_bytes_len_bytes=struct.pack("i",head_bytes_len)    # 把bytes类型的head 对应的字节长度这个整数int使用struct的pack模块转化为字节,都是对应四个字节
# server端只需要接收四个字节,就可以拿到head字节数的长度,然后再接收这个 字节数的长度 就可以完全拿到head信息
sk.send(head_bytes_len_bytes)  # 发送head报头字节数长度对应的字节(4个 里面其实代表的是表头的长度信息)
sk.send(head_bytes)  # 接下来发送head报头信息(字节类型的,server首先接收4个字节拿到head字节长度,接着接收这个字节长度 拿到head信息,bytes类型的,然后使用pickle反序列化成字典类型的head)

with open(filepath,'rb') as f:  # 打开filepath对应的文件---本地文件,,server端把需要上传的本地文件 写在py文件下的同名文件中
                                #  由于视频,音频等文件需要按照字节来读取文件,所以文件打开的方式是rb
    while filesize>=0:
        if filesize>=buffer:
            content=f.read(buffer)   # 读的内容也是二进制,bytes类型
            sk.send(content)
            filesize-=buffer
        else:
            content=f.read(filesize)
            sk.send(content)
            filesize-=buffer
sk.close()

运行结果:

 其实我只实现了上传功能,下载的原理完全一样的,直接把server 端和client端所做的工作互换就可以啦~

而且还可以加上比较人性化的交互功能,交给用户选择上传还是下载,选择路径,文件名,这样就显得高大上啦~

猜你喜欢

转载自www.cnblogs.com/xuanxuanlove/p/9750227.html