XML-RPC 是一種輕量級遠端過程調用協定,使用 HTTP 傳輸、XML 編碼。雖然主要設計用於結構化資料,但透過 xmlrpc.client.Binary 包裝器,可透明傳輸二進制資料 (圖片、檔案、序列化物件)。本文詳細講解 Python 標準庫 xmlrpc.server 與 xmlrpc.client 實現檔案傳輸的完整流程、效能考量與生產環境注意事項。
Binary 包裝器XML-RPC 規範定義 <base64> 型別對應二進制資料。Python 客戶端用 xmlrpc.client.Binary(bytes) 包裝位元組物件,序列化為 Base64 XML;伺服端自動解碼還原為 Binary 物件,透過 .data 屬性取得原始位元組。
# server.py
from xmlrpc.server import SimpleXMLRPCServer
import xmlrpc.client
import os
FILE_PATH = 'python_logo.jpg' # 需預先存在
def get_file_binary():
"""返回檔案二進制內容"""
if not os.path.exists(FILE_PATH):
raise Exception(f'檔案不存在: {FILE_PATH}')
with open(FILE_PATH, 'rb') as f:
data = f.read()
# 包裝為 Binary 物件,XML-RPC 會自動 Base64 編碼
return xmlrpc.client.Binary(data)
def get_file_info():
"""返回檔案元資訊 (不傳輸內容)"""
stat = os.stat(FILE_PATH)
return {
'filename': os.path.basename(FILE_PATH),
'size': stat.st_size,
'mtime': stat.st_mtime
}
# 建立伺服器
server = SimpleXMLRPCServer(('0.0.0.0', 8000), allow_none=True)
server.register_introspection_functions() # 支援 system.listMethods 等
server.register_function(get_file_binary, 'get_file')
server.register_function(get_file_info, 'get_file_info')
print("XML-RPC 伺服器啟動於 https://0.0.0.0:8000")
print("可用方法: get_file, get_file_info")
server.serve_forever()
# client.py
import xmlrpc.client
import os
SERVER_URL = 'https://localhost:8000/'
OUTPUT_FILE = 'downloaded_logo.jpg'
proxy = xmlrpc.client.ServerProxy(SERVER_URL, allow_none=True)
try:
# 1. 先獲取檔案資訊 (可選)
info = proxy.get_file_info()
print(f"準備下載: {info['filename']} ({info['size']} bytes)")
# 2. 下載二進制內容
print("下載中...")
binary_data = proxy.get_file() # 返回 Binary 物件
# 3. 寫入本地檔案
with open(OUTPUT_FILE, 'wb') as f:
f.write(binary_data.data) # .data 取得原始 bytes
print(f"下載完成: {OUTPUT_FILE} ({os.path.getsize(OUTPUT_FILE)} bytes)")
except xmlrpc.client.Fault as err:
print(f"RPC 錯誤: {err.faultString} (代碼: {err.faultCode})")
except ConnectionRefusedError:
print("無法連線至伺服器,請確認伺服器已啟動")
except Exception as e:
print(f"發生錯誤: {e}")
Base64 編碼會使資料膨脹 ~33%,大檔案一次性傳輸易導致記憶體耗盡。解決方案:分塊傳輸 + 斷點續傳。
CHUNK_SIZE = 1024 * 1024 # 1MB
def get_file_chunk(offset, size):
"""讀取指定位移與大小的塊"""
with open(FILE_PATH, 'rb') as f:
f.seek(offset)
chunk = f.read(size)
return xmlrpc.client.Binary(chunk)
def get_file_size():
return os.path.getsize(FILE_PATH)
server.register_function(get_file_chunk, 'get_file_chunk')
server.register_function(get_file_size, 'get_file_size')
def download_large_file():
total_size = proxy.get_file_size()
print(f"檔案大小: {total_size} bytes")
with open(OUTPUT_FILE, 'wb') as f:
offset = 0
while offset < total_size:
chunk_size = min(CHUNK_SIZE, total_size - offset)
print(f"下載位移 {offset} - {offset + chunk_size} / {total_size}")
binary_chunk = proxy.get_file_chunk(offset, chunk_size)
f.write(binary_chunk.data)
offset += chunk_size
print("大檔案下載完成")
download_large_file()
| 指標 | XML-RPC + Base64 | HTTP 直接下載 | FTP/SFTP |
|---|---|---|---|
| 傳輸開銷 | +33% (Base64) | 無 (原始二進制) | 無 |
| 記憶體使用 | 高 (全量編碼/解碼) | 低 (串流) | 低 |
| 防火牆穿透 | 優 (HTTP 80/443) | 優 | 差 (需額外埠) |
| 實現複雜度 | 低 (標準庫) | 低 | 中 |
| 斷點續傳 | 需自行實現 | 支援 Range 請求 | 支援 |
| 適用場景 | 小檔案、內網、跨語言 RPC | 檔案分發、CDN、大檔案 | 傳統檔案傳輸、批量同步 |
XML-RPC 無內建認證,需自行實現:
ServerProxy('https://user:pass@host:port/')SimpleXMLRPCServer 搭配 ssl.wrap_socket 或反向代理 (Nginx) 終結 TLS
def safe_get_file_chunk(offset, size):
# 嚴格驗證參數
if not isinstance(offset, int) or not isinstance(size, int):
raise xmlrpc.client.Fault(400, '參數必須為整數')
if offset < 0 or size <= 0 or size > 10*1024*1024: # 限制最大 10MB
raise xmlrpc.client.Fault(400, '參數超出範圍')
if offset + size > os.path.getsize(FILE_PATH):
raise xmlrpc.client.Fault(400, '超出檔案範圍')
# ... 讀取邏輯
SimpleXMLRPCServer 預設單執行緒,高併發下會阻塞ThreadingXMLRPCServer (需自行實現或用 xmlrpc.server.ThreadingMixIn)若需求為單純檔案下載,強烈建議改用 HTTP 靜態檔案服務 (Nginx、http.server、FastAPI 靜態檔案),效能高數量級,支援 Range 斷點續傳、快取、壓縮。XML-RPC 適合「檔案傳輸為 RPC 業務一部分」的場景 (如:遠端備份觸發、報表生成下載、跨語言微服務檔案交換)。
Python xmlrpc.client.Binary 提供了極簡單的二進制資料 XML-RPC 傳輸機制:伺服端 return xmlrpc.client.Binary(data),客戶端 proxy.method().data。但 Base64 開銷、記憶體壓力、無原生斷點續傳、單執行緒限制,使其僅適合小檔案 (建議 < 10MB)、低併發、內網 RPC 場景。大檔案、高效能、公開網路環境,請優先選擇 HTTP 靜態服務、gRPC 流式傳輸、或專用檔案傳輸協定。理解這些權衡,才能在架構選型時做出正確決策。