socket實現文件上傳與下載(Python)


一、客戶端從服務端下載文件(面向過程--函數版本)

server. py

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]  # '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()

client. py

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('>>> ').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)[0]
    # 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 < total_size:
            line = phone.recv(1024)
            f.write(line)
            recv_size += len(line)
            # print('總大小:%s     已下載:%s' % (total_size, recv_size))

# 關閉套接字
phone.close()

客戶端運行代碼:

下載成功后的效果:

二、客戶端向服務端下載文件(面向對象版本)

server. py

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)[0]
                    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('----->', file_path)
        with open(file_path, 'wb') as f:
            while recv_size < 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()

client. py

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(">>: ").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]
        filename = args[1]
        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()

server. py 運行結果

client. py 運行結果

上傳成功后的效果圖:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM