首页 > 编程语言 >python网络编程(四)用面向对象方式实现文件上传下载

python网络编程(四)用面向对象方式实现文件上传下载

时间:2024-01-31 15:55:11浏览次数:42  
标签:socket header python 上传下载 cmd filename 面向对象 self size

一:背景

在之前已经实现了文件的下载,现在再来完善上传功能,并且使用面向对象来封装,让代码看起来更加清楚明了。

二: 使用规则和运行结果

  • 下载文件,下载格式 get 文件名
    get空格后面直接接文件名称,在服务端存放的文件名

  • 上传文件,上传格式 put 文件路径+文件名
    因为是上传,上传的时候需要加上文件的路径和文件的名字,客户端程序可以直接根据路径去读取文件内容发送给服务端

  • 以下是本地运行的结果
    上传
    在这里插入图片描述
    下载:
    在这里插入图片描述
    在这里插入图片描述

三:部分函数说明

1、客户端

我们把套接字家族、协议类型、最大传输字节数、编码格式,和下面目录都设置为类变量

class MyTcpClient:
    address_family = socket.AF_INET
    socket_type = socket.SOCK_STREAM
    allow_reuse_address = False
    max_packet_size = 8192
    coding = 'gbk'
    downloads_dir = r'F:\myfile\python\code\python3进阶\chepter07\文件上传下载面向对象\client\downloads'
 

还把每一个连接步骤也封装成为一个方法,在初始化实例的时候,要传入服务端地址

  def __init__(self,server_address,connect=True):
        self.server_address = server_address
        self.socket = socket.socket(self.address_family, self.socket_type)
        if connect:
            try:
                self.client_connect()
            except:
                self.client_close()
                raise
 

run函数就是处理键盘输入的命令,先解析数是put 还是get,把他存入到cmd中。
hasattr() 函数用于判断对象是否包含对应的属性。
getattr其实是使用的反射,获取到cmd 是put ,func(l) 就表示是调用put(l)方法
在run 方法中只去分析命令,并调用对应的方法。

    def run(self):
        while True:
            inp = input(">>: ").strip()  # 要求下载文件的格式get a.txt,  上传的时候带文件路径如 put e:\selenium.png
            if not inp: continue  # 如果发的是空就进入下一次循环
            l = inp.split()
            cmd = l[0]
            if hasattr(self,cmd):
                func = getattr(self,cmd)
                func(l)
 

get方法,传入的参数是[‘get’,‘a.txt’], 不管是get 还是put都把命令都加上一个报头,如果{‘cmd’:cmd,‘filename’:filename,‘file_size’:10},cmd和文件名称都可以从参数中获取到,file_size在这里是没有用的,所以随便写一个都无所谓。先把报头发过去,在接收服务端返回的数据。
服务端也是先返回一个报头,告诉你文件的大小,获取到文件大小之后,再把文件写入到本地目录。

    def get(self,args):
        cmd = args[0]
        filename = args[1]
        header_dic = {'cmd':cmd,'filename':filename,'file_size':10}
        #print(header_dic)
        header_json = json.dumps(header_dic)
        header_bytes = header_json.encode(self.coding)
        self.socket.send(struct.pack('i', len(header_bytes)))
        self.socket.send(header_bytes)   #发送命令

        #接收报头
        head_struct = self.socket.recv(4)
        if not head_struct: return      # 适用于linux操作系统,如果客户端断开了连接

        header_len = struct.unpack('i', head_struct)[0]
        header_json = self.socket.recv(header_len).decode(self.coding)
        header_dic = json.loads(header_json)
        print(header_dic)
        filename = header_dic['filename']
        file_size = header_dic['file_size']

        with open('%s/%s' % (self.downloads_dir, filename), 'wb') as fd:
            recv_size = 0  # 接收的数据大小
            while recv_size < file_size:
                data = self.socket.recv(self.max_packet_size)
                fd.write(data)
                recv_size = recv_size + len(data)
                print('总大小:%s    已下载大小:%s' % (file_size, recv_size))
 

put方法,这次参数是 [‘put’,‘e:\a.txt’] 这样的,filepath就表示带路径的文件,先获取到文件大小
其实在服务端是不需要你客户端的文件路径的,只需要文件名,服务端会在指定目录下创建新的文件,所以我通过\ 来切出文件的名称,传给服务端的报头就只有文件名
把包头发送过去后,再发送文件内容。

    def put(self,args):
        cmd = args[0]
        filepath = args[1]
        if not os.path.isfile(filepath):
            print('file: %s is not exists' %filepath)
            return
        else:
            file_size = os.path.getsize(filepath)

        filename = str(filepath).split('\\')[-1]
        header_dic = {'cmd':cmd,'filename':filename,'file_size':file_size}
        print(header_dic)
        header_json = json.dumps(header_dic)
        header_bytes = header_json.encode(self.coding)
        self.socket.send(struct.pack('i', len(header_bytes)))
        self.socket.send(header_bytes)
        send_size = 0
        with open(filepath, 'rb') as fd:
            for line in fd:  # 一行一行读取
                self.socket.send(line)
                send_size = send_size + len(line)
                print('总大小:%s    已发送大小:%s' % (file_size, send_size))
 

2、服务器端

其实上传和下载的方法,在服务端跟客户端只是反一下,变化不大
服务端的run方法会比较不同
因为客户端先发的是一个报头,所以服务端就先接受4字节的包头,并且客户解析出是上传还是下载,如果是上传就调用上传的方法,如果是下载就调用下载的方法,传入的参数都是报头,如header_dic = {‘cmd’: ‘get’, ‘filename’: filename, ‘file_size’: file_size }

    def run(self):
        while True:  # 连接循环
            self.conn, self.client_addr = self.get_accept()
            print(self.client_addr)

            while True:  # 通信循环
                try:
                    # 1、接收报头的长度
                    head_struct = self.conn.recv(4)
                    if not head_struct: break  # 适用于linux操作系统,如果客户端断开了连接

                    header_len = struct.unpack('i',head_struct)[0]
                    header_json = self.conn.recv(header_len).decode(self.coding)
                    header_dic = json.loads(header_json)
                    print(header_dic)

                    # 2、解析出文件名称
                    #header_dic = {'cmd': 'get', 'filename': filename, 'file_size': file_size  }
                    cmd = header_dic['cmd']
                    if hasattr(self,cmd):
                        func = getattr(self,cmd)
                        func(header_dic)
                except ConnectionResetError:  # 适用于windows系统,如果客户端断开连接,在windows系统就会报ConnectionResetError的错误
                    break

            self.conn.close()
 

四:完整代码

服务端代码

#--coding:utf-8--
import socket
import struct
import json
import os

class MyTcpServer:
    '''
    文件上传下载的服务器端
    '''
    address_family = socket.AF_INET
    socket_type = socket.SOCK_STREAM
    allow_reuse_address = False
    max_packet_size = 8192
    coding = 'gbk'
    request_queue_size = 5
    server_dir = r'F:\myfile\python\code\python3进阶\chepter07\文件上传下载面向对象\server\share'


    def __init__(self,server_address,bind_and_activate=True):
        """
        socket 初始化
        :param server_address: 服务器地址,(ip,端口)
        :param bind_and_activate:
        """
        self.server_address = server_address
        self.socket = socket.socket(self.address_family,self.socket_type)
        if bind_and_activate:
            try:
                self.server_bind()
                self.server_listen()
            except:
                self.server_close()
                raise

    def server_bind(self):
        """绑定"""
        if self.allow_reuse_address:
            self.socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
        self.socket.bind(self.server_address)
        self.server_address = self.socket.getsockname()
        print(self.server_address)

    def server_listen(self):
        """监听"""
        self.socket.listen(self.request_queue_size)
        print("Staring")

    def server_close(self):
        """关闭socket"""
        self.socket.close()

    def get_accept(self):
        """获取跟客户端绑定的信息"""
        return self.socket.accept()


    def run(self):
        while True:  
            self.conn, self.client_addr = self.get_accept()
            print(self.client_addr)

            while True: 
                try:
                    # 1、接收报头的长度
                    head_struct = self.conn.recv(4)
                    if not head_struct: break  # 适用于linux操作系统,如果客户端断开了连接

                    header_len = struct.unpack('i',head_struct)[0]
                    header_json = self.conn.recv(header_len).decode(self.coding)
                    header_dic = json.loads(header_json)
                    print(header_dic)

                    # 2、解析出文件名称
                    #header_dic = {'cmd': 'get', 'filename': filename, 'file_size': file_size  }
                    cmd = header_dic['cmd']
                    if hasattr(self,cmd):
                        func = getattr(self,cmd)
                        func(header_dic)
                except ConnectionResetError:  # 适用于windows系统,如果客户端断开连接,在windows系统就会报ConnectionResetError的错误
                    break

            self.conn.close()

    def get(self,args):
        """读取服务端文件发送给客户端"""
        #获取文件名:
        filename = args['filename']
        # 3、获取到文件大小
        file_size = os.path.getsize('%s\%s' % (self.server_dir, filename))

        # 第一步制作固定长度的包头
        header_dic = {
            'cmd': args['cmd'],
            'filename': filename,  # a.txt
            'file_size': file_size
        }
        # 将字典转化成字符串
        header_json = json.dumps(header_dic)
        # 在将字符串转换为bytes
        header_bytes = header_json.encode(self.coding)
        # 第二步,先发送包头的长度
        self.conn.send(struct.pack('i', len(header_bytes)))
        # 第三步: 发送报头
        self.conn.send(header_bytes)
        # 第四步:读取文件内容,发送给客户端
        with open('%s\%s' % (self.server_dir, filename), 'rb') as fd:
            for line in fd:  # 一行一行读取
                self.conn.send(line)

    def put(self,args):
        filename = args['filename']    # e:\selenium.png
        file_size = args['file_size']

        with open('%s\%s' % (self.server_dir, filename),'wb') as fd:
            recv_size = 0  # 接收的数据大小
            while recv_size < file_size:
                data = self.conn.recv(self.max_packet_size)
                fd.write(data)
                recv_size = recv_size + len(data)
                print('总大小:%s    已下载大小:%s' % (file_size,recv_size))
      
if __name__ == '__main__':
    tcpserver1 = MyTcpServer(('127.0.0.1',8891))
    tcpserver1.run()
 

客户端代码

import socket
import struct
import json
import os

class MyTcpClient:
    address_family = socket.AF_INET
    socket_type = socket.SOCK_STREAM
    allow_reuse_address = False
    max_packet_size = 8192
    coding = 'gbk'
    downloads_dir = r'F:\myfile\python\code\python3进阶\chepter07\文件上传下载面向对象\client\downloads'

    def __init__(self,server_address,connect=True):
        self.server_address = server_address
        self.socket = socket.socket(self.address_family, self.socket_type)
        if connect:
            try:
                self.client_connect()
            except:
                self.client_close()
                raise

    def client_connect(self):
        self.socket.connect(self.server_address)

    def client_close(self):
        self.socket.close()

    def run(self):
        while True:
            inp = input(">>: ").strip() 
            if not inp: continue  # 如果发的是空就进入下一次循环
            l = inp.split()
            cmd = l[0]
            if hasattr(self,cmd):
                func = getattr(self,cmd)
                func(l)

    def put(self,args):
        cmd = args[0]
        filepath = args[1]
        if not os.path.isfile(filepath):
            print('file: %s is not exists' %filepath)
            return
        else:
            file_size = os.path.getsize(filepath)

        filename = str(filepath).split('\\')[-1]
        header_dic = {'cmd':cmd,'filename':filename,'file_size':file_size}
        print(header_dic)
        header_json = json.dumps(header_dic)
        header_bytes = header_json.encode(self.coding)
        self.socket.send(struct.pack('i', len(header_bytes)))
        self.socket.send(header_bytes)
        send_size = 0
        with open(filepath, 'rb') as fd:
            for line in fd:  # 一行一行读取
                self.socket.send(line)
                send_size = send_size + len(line)
                print('总大小:%s    已发送大小:%s' % (file_size, send_size))

    def get(self,args):
        cmd = args[0]
        filename = args[1]
        header_dic = {'cmd':cmd,'filename':filename,'file_size':10}
        #print(header_dic)
        header_json = json.dumps(header_dic)
        header_bytes = header_json.encode(self.coding)
        self.socket.send(struct.pack('i', len(header_bytes)))
        self.socket.send(header_bytes)   #发送命令

        #接收报头
        head_struct = self.socket.recv(4)
        if not head_struct: return      # 适用于linux操作系统,如果客户端断开了连接

        header_len = struct.unpack('i', head_struct)[0]
        header_json = self.socket.recv(header_len).decode(self.coding)
        header_dic = json.loads(header_json)
        print(header_dic)
        filename = header_dic['filename']
        file_size = header_dic['file_size']

        with open('%s/%s' % (self.downloads_dir, filename), 'wb') as fd:
            recv_size = 0  # 接收的数据大小
            while recv_size < file_size:
                data = self.socket.recv(self.max_packet_size)
                fd.write(data)
                recv_size = recv_size + len(data)
                print('总大小:%s    已下载大小:%s' % (file_size, recv_size))

if __name__ == '__main__':
    tcpclient1 = MyTcpClient(('127.0.0.1',8891))
    tcpclient1.run()

 

2024-01-31 15:52:01【出处】:https://blog.csdn.net/javascript_good/article/details/131461941

=======================================================================================

标签:socket,header,python,上传下载,cmd,filename,面向对象,self,size
From: https://www.cnblogs.com/mq0036/p/17999439

相关文章

  • python将pdf每页截图保存
    python将pdf每页保存成图片保存一、安装依赖包pipinstallpdf2image 二、代码importosfrompdf2imageimportconvert_from_pathdefconvert_pdf_to_images(pdf_file,output_folder):#创建输出文件夹os.makedirs(output_folder,exist_ok=True)#......
  • python识别图片中的文本保存到word中
    python可以使用第三方库pytesseract实现图像的文本识别,并将识别的结果保存到word中,代码本生不复杂pytesseract环境有点麻烦这里整理总结一下一、简介Tesseract是一个由HP实验室开发由Google维护的开源的光学字符识别(OCR)引擎,可以在Apache2.0许可下获得。它可以直接使用,或者(......
  • python中设置cudnn作用理解
     1、cudnn的简介cuDNN(CUDADeepNeuralNetworklibrary):是NVIDIA打造的针对深度神经网络的加速库,是一个用于深层神经网络的GPU加速库。如果你要用GPU训练模型,cuDNN不是必须的,但是一般会采用这个加速库。2、torch.backends.cudnn的理解 cuDNN使用非确定性算法,并且可以使用to......
  • Python+Selenium 自动化测试
    自动化测试是把以人为驱动的测试行为转化成机器执行的一种过程,通常在设计了测试用例并通过评审之后,由测试人员根据测试用例中描述的规程一步步执行测试,得到实际结果与期望结果的比较,再此过程中,为了节省人力,时间或硬件资源,提高测试效率,便引用了自动化测试的概念Selenium:是一套代码......
  • `glob`和`fnmatch`都是Python的内置模块,用于文件名的匹配,但它们的功能和使用场景有所
    `glob`和`fnmatch`都是Python的内置模块,用于文件名的匹配,但它们的功能和使用场景有所不同²。1.**fnmatch**:`fnmatch`模块提供了一种简单的方式来匹配Unixshell风格的模式,如`*.py`,`Dat[0-9]*`,`Dat[!0-9]*`等²。它只是将一个文件名与模式进行比较,返回True或False²。例如,......
  • Python工具箱系列(四十九)
    使用PIL进行图片格式与尺寸转换现实世界中,图片是经常需要处理的二进制文件类型。从计算机发展的历史来看,图片的格式丰富多彩,但大体来说分成两类:•位图格式•矢量格式矢量格式如svg等,能够随意放大缩小而不变形,原因在于矢量格式描述了如何产生图形的方法。而位图格式(例如BMP/JPEG/PN......
  • 解决gpt返回json Python没法解析的情况
    importreimportjsondefreplace_newlines(match):#在匹配的字符串中替换\n和\rreturnmatch.group(0).replace('\n','\\n').replace('\r','\\r')defclean_json_str(json_str:str)->str:""&......
  • Python工具箱系列(四十九)
    使用PIL进行图片格式与尺寸转换现实世界中,图片是经常需要处理的二进制文件类型。从计算机发展的历史来看,图片的格式丰富多彩,但大体来说分成两类:•位图格式•矢量格式矢量格式如svg等,能够随意放大缩小而不变形,原因在于矢量格式描述了如何产生图形的方法。而位图格式(例如BMP/J......
  • 17个工作必备的Python自动化代码
    您是否厌倦了在日常工作中做那些重复性的任务?简单但多功能的Python脚本可以解决您的问题。 我们将通过上下两个篇章为您介绍17个能够自动执行各种任务并提高工作效率Python脚本及其代码。无论您是开发人员、数据分析师,还是只是希望简化工作流程的人,这些脚本都能满足您的需求。 ......
  • python selenium i 不再自动退出
    官网https://chromedriver.chromium.org/home  chrome版本  版本109.0.5414.120(正式版本)(64位) driver 版本indexof/109.0.5414.74/  ##导入selenium#fromseleniumimportwebdriver##选择谷歌浏览器#driver_path=r"C:\ProgramFiles\Googl......