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

news2024/11/15 23:50:32

一:背景

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

二: 使用规则和运行结果

  • 下载文件,下载格式 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()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/701024.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL-SQL InnoDB引擎 (下)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a; 小刘主页 ♥️努力不一定有回报&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️学习两年总结出的运维经验&#xff0c;以及思科模拟器全套网络实验教程。专栏&#xf…

【直播预告】HarmonyOS极客松赋能直播第三期:一次开发多端部署与ArkTS卡片开发

直播预约通道&#xff1a; 【直播预告】HarmonyOS极客松赋能直播第三期&#xff1a;一次开发多端部署与ArkTS卡片开发

朴素模式匹配算法(暴力寻找字串)

目录 0. 前言1. 算法简介2. 代码实现3. 运行结果 0. 前言 使用朴素模式匹配算法查找子串是否位于主串中 开发环境&#xff1a;Dev-Cpp 操作系统&#xff1a;Windows10 专业版 1. 算法简介 朴素模式匹配算法&#xff0c;也称为暴力模式匹配算法或穷举法&#xff0c;是一种简…

自己动手写C语言float浮点数转换字符串的函数

最近在项目中用到了holtek厂商的HT45F24A和BA45F5650两款单片机。 用的开发工具是HT-IDE3000&#xff0c;烧录软件是HOPE3000。 这两款单片机都是8位的单片机&#xff0c;支持寄存器位操作。 HT45F24A单片机不带UART串口&#xff0c;要想实现串口功能&#xff0c;只能自己用定时…

基于肺部图片与文本信息的多模态模型架构

文章题为 「A transformer-based representation learning model with unified processing of multimodal input for clinical diagnostics」 https://www.nature.com/articles/s41551-023-01045-x &#xff08;arXiv版链接: https://arxiv.org/abs/2306.00864&#xff09; htt…

2020年全国硕士研究生入学统一考试管理类专业学位联考数学试题——解析版

2020 级考研管理类联考数学真题 一、问题求解&#xff08;本大题共 15 小题&#xff0c;每小题 3 分&#xff0c;共 45 分&#xff09;下列每题给出 5 个选项中&#xff0c;只有一个是符合要求的&#xff0c;请在答题卡上将所选择的字母涂黑。 1、某产品去年涨价 10%&#xf…

备战2024秋招面试题-Vue的框架原理

前言&#xff1a; \textcolor{Green}{前言&#xff1a;} 前言&#xff1a; &#x1f49e;快秋招了&#xff0c;那么这个专栏就专门来记录一下&#xff0c;同时呢整理一下常见面试题 &#x1f49e;部分题目来自自己的面试题&#xff0c;部分题目来自网络整理 给我冲 学习目标&am…

阿里云服务器ECS介绍_云主机_服务器托管_弹性计算

阿里云服务器安全可靠、弹性可伸缩&#xff0c;CPU可选256核、内存选到3072GB&#xff0c;云服务器ECS规格通用型、计算型、内存型、通用算力型、裸金属、GPU、大数据等ECS实例规格&#xff0c;公网带宽可选到200M&#xff0c;绑定弹性公网EIP带宽可达1000M&#xff0c;共享带宽…

9.外部中断

1.中断概念&#xff1a; &#xff08;1&#xff09;STM32的每个IO口都可以作为外部中断输入&#xff1b; &#xff08;2&#xff09;stm32的中断控制器支持19个外部中断/事件请求 线0~15&#xff1a;对应外部IO口的输入中断&#xff1b;线16&#xff1a;连接到PVD输出&#…

基于jsp+Servlet+mysql的汽车销售系统

基于jspServletmysql的汽车销售系统 一、系统介绍二、功能展示1.项目骨架2.登录界面3.首页4.购物车5.添加车辆6、编辑车辆信息 四、其它1.其他系统实现五.获取源码 一、系统介绍 项目类型&#xff1a;Java web项目 项目名称&#xff1a;基于JSPServlet的汽车销售系统 项目架…

新后端漏洞之----SSRF漏洞(服务端请求伪造)

笔记 前言SSRF漏洞概述SSRF漏洞检测与挖掘SSRF漏洞的回显分类SSRF漏洞利用SSRF漏洞防御 前言 这几天各种技术面试接踵而至&#xff0c;压得我喘不过气了&#xff01;然后面试官问了我这个SSRF漏洞原理和利用方式以及防御手段&#xff0c;当然同时还问了好几个Top10漏洞&#x…

【React】React Hooks解析

React Hooks解析 React 16.8 认识和体验Hooks 为什么需要Hook? Hook是 React 16.8 的新增特性&#xff0c;它可以让我们在不编写class的情况下使用state以及其他的React特性&#xff08;比如生命周期&#xff09; 我们先来思考一下class组件相对于函数式组件有什么优势&…

企业知识竞赛答题pk活动怎么做?

随着互联网的发展&#xff0c;越来越多的企事业单位开始利用答题小程序进行线上PK答题活动&#xff0c;目的在于组织员工学习企业文化或是进行专题答题活动以适应时代的进步。其中最主流的有&#xff1a;网络安全知识竞赛、安全生产知识竞赛、企业文化PK答题竞赛、红色党史知识…

js中的树以及优先遍历!

树 什么是树&#xff1f; 在生活中&#xff0c;大家对树肯定不陌生&#xff0c;小朋友都知道树不就是一类植物嘛&#xff0c;不管在任何地方都有各种各样的树。但是在计算机科学里面树是什么呢&#xff1f;一种分层数据的抽象模型&#xff0c;在我们前端工作中无处不在。在 J…

攻克数据中心液冷升级三大难题,宁畅推出“无忧焕液计划“

近年来&#xff0c;在政策引导、市场需求、技术升级等多种因素影响下&#xff0c;数据中心正在迎来发展新机遇。如何部署节能技术并兼顾算效、能耗、成本&#xff0c;成为考验数据中心建设与运营者的关键。 在此背景下&#xff0c;宁畅于6月28日召开“无忧焕液 智惠升级”媒体沟…

epoll反应堆

// epoll基于非阻塞I/O事件驱动 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <unistd.h> #include <arpa/inet.h> #include <netinet/in.h> #include <sys/epoll.h> #inclu…

利用Python分析快手APP全国大学生用户数据

背景 背景&#xff1a;利用Python分析快手APP全国大学生用户数据&#xff0c;发现&#xff1a; 哪个学校的学生最喜欢使用快手APP Android、IOS、PC三大平台用户占比份额 全国哪些城市(学校所在地)的学生使用频次最高 全国哪些省份的生源最喜欢使用快手APP … 数据&#xff1a…

形式化验证,Gap-free Processor Verifification by S2QED and Property Generation(一)

目录 一、Article:文献出处&#xff08;方便再次搜索&#xff09; &#xff08;1&#xff09;作者 &#xff08;2&#xff09;文献题目 &#xff08;3&#xff09;文献时间 &#xff08;4&#xff09;引用 二、Data:文献数据&#xff08;总结归纳&#xff0c;方便理解&am…

不漏测,测试人员的极致追求

一、什么是漏测&#xff1f; 具体地说&#xff0c;什么是测试漏测&#xff1f;测试漏测是指软件产品在测试结束后出现了在测试过程中没有被发现的bug。 我们知道&#xff0c;漏测是每一个软件测试者最头疼的事&#xff0c;一旦出现漏测&#xff1a; 首先给客户带来了非常不好…

C语言-基础语法学习-3 二级指针

目录 二级指针二级指针的定义和声明二级指针的初始化二级指针的使用二级指针和函数参数二级指针和动态内存分配数组指针二维数组二维数组的初始化二维数组与指针二维数组的遍历 二级指针 当涉及到多级指针时&#xff0c;C语言的灵活性和强大的指针功能可以得到充分的发挥。二级…