透過Socket傳送檔案:簡單檔案傳輸的方法

目錄
前陣子想要透過 socket 傳送檔案
於是找了下資料
做個筆記
傳送方法
在使用 socket 接收字串的時候,由於不知道這個訊息的字串有多長
我們可能會用換行 (\n) 來當作分隔兩個訊息的分隔符號 (delimiter)
但是這種做法要確保你傳送的訊息內不能有分隔符號
如果訊息內有包含分隔符號就需要另外做判斷
因此這種做法不太好,我們可以試試看以下方法:
在傳送資料之前,我們可以先傳送資料長度
接著再傳送資料本體
如此一來,接收方就知道這次訊息有多少長度
就不會混淆兩個連續的訊息了
傳送資料
要想透過 socket 傳送檔案,我們需要傳送以下資料:
- 檔案名稱
- 檔案大小
- 檔案內容
由於 socket 傳送時要使用 byte 傳送
檔案名稱我們可以透過 encode
轉成 bytes
接收後再透過 decode
轉回 string
fileName = 'test.txt'
fileName_bytes = fileName.encode('utf-8')
fileName_str = fileName_bytes.decode('utf-8')
我們可以透過 os.path.getsize
取得檔案大小
接著再透過 to_bytes
轉成 bytes
這時候要注意我們要轉成的 bytes 的大小
一般使用 4 個 bytes 即可,也就是用 32 個 bit 表示數字
如果你的檔案大小更大的話,可以往上調整表示的 bytes 數量
接著要設定 bytes 的排列順序,這裡使用的是 little endian
你也可以設定成 big endian,只要接收和傳送是一致的就好
最後透過 int.from_bytes
將 bytes 轉回 int
import os
fileSize = os.path.getsize('test.txt')
fileSize_bytes = fileSize.to_bytes(4, 'little')
fileSize_int = int.from_bytes(fileSize_bytes, 'little')
最後傳送檔案本體就好,記得要用 binary 的方式開啟
接收時,輸出也要是 binary 方式
with open(FILE, "rb") as f:
bytes = f.read(1024)
while bytes:
bytes = f.read(1024)
with open(file_name, "wb") as f:
while True:
bytes = client.recv(1024)
file_size -= len(bytes)
f.write(bytes)
if file_size == 0: break
Example
以下簡單寫了個 Server 和 Client 的範例
Server:
import socket
import os
HOST = '127.0.0.1'
PORT = 8000
FILE = "file.data"
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((HOST, PORT))
server.listen(1)
conn, addr = server.accept()
with open(FILE, "rb") as f:
# file name
file_name_bytes = FILE.encode("utf-8")
file_name_bytes_len = len(file_name_bytes)
conn.sendall(file_name_bytes_len.to_bytes(4, 'little'))
conn.sendall(file_name_bytes)
print(f"File Name: {FILE}")
# file size
file_size = os.path.getsize(FILE)
conn.sendall(file_size.to_bytes(4, 'little'))
print(f"File Size: {file_size} (bytes)")
# file content
bytes = f.read(1024)
while bytes:
conn.sendall(bytes)
bytes = f.read(1024)
conn.close()
Client:
import socket
HOST = "127.0.0.1"
PORT = 8000
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((HOST, PORT))
# file name
file_name_bytes_len = int.from_bytes(client.recv(4), "little")
file_name = client.recv(file_name_bytes_len).decode("utf-8")
print(f"File Name: {file_name}")
# file size
file_size = int.from_bytes(client.recv(4), "little")
print(f"File Size: {file_size} (bytes)")
# file content
with open(file_name, "wb") as f:
while True:
bytes = client.recv(1024)
file_size -= len(bytes)
f.write(bytes)
if file_size == 0: break
client.close()
Reference
如果你覺得這篇文章有用 可以考慮贊助飲料給大貓咪