微醺的饮者 發表於 2020-3-22 14:12:00

socket实现文件上传与下载(Python)

<h2 id="一客户端从服务端下载文件面向过程--函数版本">一、客户端从服务端下载文件(面向过程--函数版本)</h2>
<p>server. py</p>
<pre><code class="language-python">import socket
import json
import struct
import os

# 定义路径全局变量,这里为服务端提供文件的路径
share_dir = r'/Users/xiexinran/Desktop/乱七八糟的.py/socket/server/Share'

# 建立
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# 绑定
phone.bind(('127.0.0.1', 8081))

# 监听
phone.listen(5)

# 通信循环
while True:
    # 接收客户端连接请求
    conn, client_addr = phone.accept()
    while True:
      # 接收客户端数据/命令
      res = conn.recv(1024)
      if not res:
            continue
      # 解析命令 'get 1.mp4'
      cmds = res.decode('utf-8').split()# ['get','1.mp4']
      filename = cmds# '1.mp4'
      # 以读的方式打开文件,提取文件内容发送给客户端
      # 1.制作固定长度的报头
      header_dic = {
            'filename': filename,
            'file_size': os.path.getsize('{}/{}'.format(share_dir, filename))
      }
      # 序列化报头
      header_json = json.dumps(header_dic)# 序列化为byte字节流类型
      header_bytes = header_json.encode('utf-8')# 编码为utf-8(Mac系统)
      # 2.先发送报头的长度
      # 2.1 将byte类型的长度打包成4位int
      conn.send(struct.pack('i', len(header_bytes)))
      # 2.2 再发报头
      conn.send(header_bytes)
      # 2.3 再发真实数据
      with open('{}/{}'.format(share_dir, filename), 'rb') as f:
            for line in f:
                conn.send(line)
    # 结束连接
    conn.close()

# 关闭套接字
phone.close()
</code></pre>
<p>client. py</p>
<pre><code class="language-python">import socket
import struct
import json

# 定义路径全局变量,这里为客户端下载文件到本地的保存路径
Download_dir = r'/Users/xiexinran/Desktop/乱七八糟的.py/socket/client/Download'
# 建立
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 连接
phone.connect(('127.0.0.1', 8081))
while True:
    cmd = input('&gt;&gt;&gt; ').strip()
    if not cmd:
      continue
    if cmd == 'quit':
      break
    # 给服务端发送命令
    phone.send(cmd.encode('utf-8'))
    # 接收服务端数据

    # 1.先收报头长度
    obj = phone.recv(4)
    header_size = struct.unpack('i', obj)
    # 2.收报头
    '''
            header_dic = {
            'filename': filename,
            'file_size': os.path.getsize(filename)
      }
    '''
    header_bytes = phone.recv(header_size)
    # 3.从报头中解析出数据的真实信息(报头字典)
    header_json = header_bytes.decode('utf-8')
    header_dic = json.loads(header_json)
    # 4.解析命令
    total_size = header_dic['file_size']
    filename = header_dic['filename']

    # 4.接受真实数据
    with open('%s/%s' % (Download_dir, filename), 'wb') as f:
      recv_size = 0
      while recv_size &lt; total_size:
            line = phone.recv(1024)
            f.write(line)
            recv_size += len(line)
            # print('总大小:%s   已下载:%s' % (total_size, recv_size))

# 关闭套接字
phone.close()
</code></pre>
<p>客户端运行代码:</p>
<img src="https://img2020.cnblogs.com/blog/1923873/202003/1923873-20200322140828823-1230057609.png" width="200">
<p>下载成功后的效果:</p>
<img src="https://img2020.cnblogs.com/blog/1923873/202003/1923873-20200322140908463-1602132354.png" width="200">
<img src="https://img2020.cnblogs.com/blog/1923873/202003/1923873-20200322140915761-1499881611.png" width="200">
<h2 id="二客户端向服务端下载文件面向对象版本">二、客户端向服务端下载文件(面向对象版本)</h2>
<p>server. py</p>
<pre><code class="language-python">import socket
import struct
import json
import os


class MYTCPServer:
    # AF_INET IPv4因特网协议
    address_family = socket.AF_INET
    # SOCK_STREAM 提供顺序的,可靠的双向的基于连接的字节流。可能支持带外数据传输机制。
    socket_type = socket.SOCK_STREAM
    # 一次性允许传输的最大字节数
    max_packet_size = 8192
    # 编码方式
    coding = 'utf-8'
    # 最大连接数
    request_queue_size = 5
    # 服务端文件url,这里填写自己本地服务器提供的上传文件夹
    server_dir = '/Users/xiexinran/Desktop/乱七八糟的.py/socket/server2/file_upload'

    def __init__(self, server_address, bind_and_activate=True):
      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_activate()
            except:
                self.server_close()
                raise# 中断程序

    def server_bind(self):
      """
      由构造函数调用以绑定套接字
      """
      self.socket.bind(self.server_address)
      self.server_address = self.socket.getsockname()

    def server_activate(self):
      """
      由构造函数调用监听
      """
      self.socket.listen(self.request_queue_size)

    def server_close(self):
      """
      由构造函数调用关闭服务器套接字
      """
      self.socket.close()

    def get_request(self):
      """
      接收客户端请求
      """
      return self.socket.accept()

    def close_request(self, request):
      """
      关闭单个客户端请求
      """
      request.close()

    def run(self):
      while True:
            self.conn, self.client_addr = self.get_request()
            print('from client ', self.client_addr)
            while True:
                try:
                  head_struct = self.conn.recv(4)# 收客户端的报头长度
                  if not head_struct:
                        break
                  head_len = struct.unpack('i', head_struct)
                  head_json = self.conn.recv(head_len).decode(self.coding)# 收客户端的序列化报头
                  head_dic = json.loads(head_json)# 反序列化报头

                  print(head_dic)
                  # head_dic = {'cmd':'put','filename':'a.txt','filesize':123123}
                  cmd = head_dic['cmd']
                  if hasattr(self, cmd):
                        func = getattr(self, cmd)
                        func(head_dic)
                except Exception:
                  break

    def put(self, args):
      # 规范path字符串形式,把目录和文件名合成一个路径
      file_path = os.path.normpath(os.path.join(
            self.server_dir,
            args['filename']
      ))

      filesize = args['filesize']
      recv_size = 0
      print('-----&gt;', file_path)
      with open(file_path, 'wb') as f:
            while recv_size &lt; filesize:
                recv_data = self.conn.recv(self.max_packet_size)
                f.write(recv_data)
                recv_size += len(recv_data)
                # print('recvsize:%s filesize:%s' % (recv_size, filesize))


tcpserver1 = MYTCPServer(('127.0.0.1', 8080))

tcpserver1.run()
</code></pre>
<p>client. py</p>
<pre><code class="language-python">import socket
import struct
import json
import os


class MYTCPClient:
    address_family = socket.AF_INET

    socket_type = socket.SOCK_STREAM

    max_packet_size = 8192

    coding = 'utf-8'

    request_queue_size = 5

    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("&gt;&gt;: ").strip()
            if not inp:
                continue
            l = inp.split()
            cmd = l
            if hasattr(self, cmd):
                func = getattr(self, cmd)
                func(l)

    def put(self, args):
      cmd = args
      filename = args
      if not os.path.isfile(filename):# 判断路径是否为文件
            print('file:%s is not exists' % filename)
            return
      else:
            filesize = os.path.getsize(filename)

      head_dic = {'cmd': cmd, 'filename': os.path.basename(filename), 'filesize': filesize}# 返回文件名
      # print(head_dic)
      head_json = json.dumps(head_dic)
      head_json_bytes = bytes(head_json, encoding=self.coding)

      head_struct = struct.pack('i', len(head_json_bytes))
      self.socket.send(head_struct)
      self.socket.send(head_json_bytes)
      send_size = 0
      with open(filename, 'rb') as f:
            for line in f:
                self.socket.send(line)
                send_size += len(line)
                # print(send_size)
            else:
                print('upload successful')


client = MYTCPClient(('127.0.0.1', 8080))

client.run()
</code></pre>
<p>server. py 运行结果</p>
<img src="https://img2020.cnblogs.com/blog/1923873/202003/1923873-20200322141028484-1768031453.png" width="500">
<p>client. py 运行结果</p>
<img src="https://img2020.cnblogs.com/blog/1923873/202003/1923873-20200322141051984-322593276.png" width="500">
<p>上传成功后的效果图:</p>
<img src="https://img2020.cnblogs.com/blog/1923873/202003/1923873-20200322141102769-377146652.png" width="200">
<img src="https://img2020.cnblogs.com/blog/1923873/202003/1923873-20200322141112236-1603223121.png" width="200"><br><br>
来源:https://www.cnblogs.com/rainbow-ran/p/12545789.html
頁: [1]
查看完整版本: socket实现文件上传与下载(Python)