Python3 網絡編程 socket 庫使用TCP協議實現文件上傳、進度條、大文件斷點續傳

1、基於socket庫 TCP 協議實現普通小文件上傳

客戶端代碼:

# tcp_small_file_client.py
import socket
import os
import json

client = socket.socket()
client.connect(('127.0.0.1', 9090))
menu = {"1":"upload","2":"download","3":"exit"}
for key,val in menu.items():
    print(key, val)

while True:
    option = input('請輸入功能:')
    if option == '1':
        file_path = input('請輸入要上傳文件的絕對路徑:')
        file_name = os.path.basename(file_path)
        with open(file_path,'r') as f:
            file_data = f.read()
        dic = {"option":"1","name":file_name,"data":file_data}
        client.send(json.dumps(dic).encode())
        msg = client.recv(1024)
        if msg.decode() == 'ok':
            print('上傳完成...')
    elif option == '2':
        print('暫未開放...')
    elif option == '3':
        break
    else:
        print('不支持的功能,請重新輸入。')
client.close()

服務端代碼:

# tcp_small_file_server.py
import socket
import os
import json

server = socket.socket() # 默認就是tcp
server.bind(('127.0.0.1', 9090))
server.listen()

print('服務啓動...')
while True:
    conn, addr = server.accept()
    print('新的客戶端:', addr)
    while True:
        try:
            data = conn.recv(2048)
            dic = json.loads(data.decode())
            if dic.get('option') == '1':
                base_path = os.path.dirname(os.path.abspath(__file__))
                file_path = os.path.join(base_path,dic.get('name'))
                with open(file_path,'w') as f:
                    f.write(dic.get('data'))
                conn.send('ok'.encode())
        except Exception as e:
            print('有連接出現異常斷開:', str(e))
            break
    conn.close()
server.close()

運行:
先運行服務端

python tcp_small_file_server.py

再運行客戶端,並輸入對於的功能上傳文件

python tcp_small_file_client.py

2、基於socket庫 TCP 協議實現大文件上傳,並引入進度條

客戶端代碼:

# tcp_big_file_client.py
import socket
import os
import json
import time


def print_bar1(percent):
    bar = '\r' + '*' * int((percent * 100)) + ' %3.0f%%|' % (percent*100) + '100%'
    print(bar, end='', flush=True)


client = socket.socket()
client.connect(('127.0.0.1', 9090))
menu = {"1":"upload","2":"download","3":"exit"}
for key,val in menu.items():
    print(key, val)

while True:
    option = input('請輸入功能:')
    if option == '1':
        file_path = input('請輸入要上傳文件的絕對路徑:')
        file_name = os.path.basename(file_path)
        file_size = os.path.getsize(file_path)
        dic = {"option":"1","name":file_name,"file_size":file_size}
        client.send(json.dumps(dic).encode())
        msg = client.recv(100) # 判斷服務端準備好接收文件了,還可以防止粘包
        begin_size = file_size
        if msg.decode() == 'ok':
            # 服務端表示準備好接收文件了,開始循環發送文件
            with open(file_path, 'rb') as f:
                while file_size:
                    content = f.read(1024)
                    client.send(content)
                    file_size -= len(content)
                    print_bar1(round((begin_size - file_size) / begin_size, 2))
                print('')
    elif option == '2':
        print('暫未開放...')
    elif option == '3':
        break
    else:
        print('不支持的功能,請重新輸入。')
client.close()

服務端代碼:

# tcp_big_file_server.py
import socket
import os
import json
import time

server = socket.socket() # 默認就是tcp
server.bind(('127.0.0.1', 9090))
server.listen()

print('服務啓動...')
while True:
    conn, addr = server.accept()
    print('新的客戶端:', addr)
    while True:
        try:
            data = conn.recv(2048)
            dic = json.loads(data.decode())
            if dic.get('option') == '1':
                base_path = os.path.dirname(os.path.abspath(__file__))
                file_path = os.path.join(base_path,dic.get('name'))
                conn.send('ok'.encode()) # 告訴客戶端,準備好接收文件了
                # 準備接收發來的文件內容
                with open(file_path, 'ab') as f:
                    while dic['file_size']:
                        content = conn.recv(1024)
                        f.write(content)
                        dic['file_size'] -= len(content)
        except Exception as e:
            print('有連接出現異常斷開:', str(e))
            break
    conn.close()
server.close()

運行同上,先運行服務端,再運行客戶端。這次不同的時客戶端有上傳進度條,可視化查看上傳進度。

3、基於socket庫 TCP 協議實現大文件上傳,並支持斷點續傳

客戶端代碼:

# tcp_continue_big_file_client.py
import socket
import os
import json
import time


def print_bar1(percent):
    bar = '\r' + '*' * int((percent * 100)) + ' %3.0f%%|' % (percent*100) + '100%'
    print(bar, end='', flush=True)


client = socket.socket()
client.connect(('127.0.0.1', 9090))
menu = {"1":"upload","2":"download","3":"exit"}
for key,val in menu.items():
    print(key, val)

while True:
    option = input('請輸入功能:')
    if option == '1':
        file_path = input('請輸入要上傳文件的絕對路徑:')
        file_name = os.path.basename(file_path)
        file_size = os.path.getsize(file_path)
        dic = {"option":"1","name":file_name,"file_size":file_size}
        client.send(json.dumps(dic).encode())
        file_seek = int(client.recv(100).decode())
        if file_seek == file_size:
            print('文件已經存在服務端,退出此次傳輸...')
        else:
            new_size = file_size - file_seek
            begin_size = new_size
            # 服務端表示準備好接收文件了,開始循環發送文件
            with open(file_path, 'rb') as f:
                f.seek(file_seek)
                while new_size:
                    content = f.read(1024)
                    client.send(content)
                    new_size -= len(content)
                    print_bar1(round((begin_size - new_size) / begin_size, 2))
                    time.sleep(0.2)
                print('')
    elif option == '2':
        pass
    elif option == '3':
        print('暫未開放...')
    else:
        print('不支持的功能,請重新輸入。')
client.close()

服務端代碼:

# tcp_continue_big_file_server.py
import socket
import os
import json
import time

server = socket.socket() # 默認就是tcp
server.bind(('127.0.0.1', 9090))
server.listen()

print('服務啓動...')
while True:
    conn, addr = server.accept()
    print('新的客戶端:', addr)
    while True:
        try:
            is_conn = True
            data = conn.recv(2048)
            dic = json.loads(data.decode())
            if dic.get('option') == '1':
                base_path = os.path.dirname(os.path.abspath(__file__))
                file_path = os.path.join(base_path,dic.get('name'))
                if os.path.exists(file_path):
                    file_seek = os.path.getsize(file_path)
                else:
                    file_seek = 0
                # 將文件指針發送過去,同時也可以解決粘包
                conn.send(str(file_seek).encode())
                if file_seek == dic['file_size']:
                    print('文件已經傳輸完成,退出此次傳輸...')
                else:
                    # 重新設置需要接收的文件大小
                    new_size = dic['file_size'] - file_seek
                    # 準備接收發來的追加文件內容
                    with open(file_path, 'ab') as f:
                        while new_size:
                            content = conn.recv(1024)
                            f.write(content)
                            new_size -= len(content)
                            # 因爲Python中recv()是阻塞的,只有連接斷開或異常時,接收到的是b''空字節類型,因此需要判斷這種情況就斷開連接。
                            if content == b'':
                                is_conn = False
                                break
            if not is_conn:
                print('有連接客戶端斷開...')
                break
        except Exception as e:
            print('有連接出現異常斷開:', str(e))
            break
    conn.close()
server.close()

運行同上,先運行服務端,再運行客戶端。這次不同的時客戶端有上傳進度條,可視化查看上傳進度。
這裏爲了要測試斷點續傳,在客戶端代碼中每發一次數據都會停頓0.2秒,在測試上傳時,在進度條還沒完成就手動kill調客戶端。去看看服務端看文件存到什麼程度了,再次運行客戶端上傳上一次爲傳輸完成的文件,等傳輸完成後。再去服務端看目標文件是否完整、有效。

好了,到這裏就基本完成我們需要的功能,小夥伴們可以在本地做測試,筆者的環境是Ubuntu + Python3.8。有興趣的小夥伴可以把下載功能實現了,有啥問題大家可以多交流學習。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章