gRPC 客户端(client)从服务端(server)下载文件 python 简单实现
前提准备:
1、gRPC 环境
2、python 环境
3、protobuf tools 生成 python 代码工具
一、编写 Protocol Buffers 文件并编译
编写用于生成 server 与 client 接口代码的 gRPC IDL:file.proto 文件:
创建 file.proto 文件,示例代码:
syntax = "proto3";
package filetransfer;
// file request
message FileRequest {
string file_path = 1;
string file_name = 2;
}
// file response
message FileResponse {
bytes file_content = 1;
}
// the service of file transfer
service FileTransfer {
// Download file from server to client
rpc DownloadFile(FileRequest) returns (FileResponse);
}
此文件定义了服务类 FileTransfer 及其实现的方法 DownloadFile 并指定了传入参数 FileRequest 与返回值 FileResponse。
FileRequest 包含两个 string 类型字符串,分别表示想要下载的文件名及其在服务端的存放路径,FileResponse 包含一个 bytes 类型变量,用于传输文件内容。
在 file.proto 目录下启动 powershell 或命令行执行 file.proto 文件编译命令:
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. file.proto
此命令会在当前目录下生成两个文件:file_pb2.py 和 file_pb2_grpc.py,它们包含了基于 Protocol Buffers 定义的消息类型和 gRPC 服务的代码。其中 --python_out 为 file_pb2.py 的输出路径,--grpc_python_out 为 file_pb2_grpc.py 的输出路径。
二、编写 server.py 实现具体业务逻辑
在定义服务接口后,server.py 负责具体服务逻辑的实现。
创建 server.py 文件,示例代码:
import grpc
from concurrent import futures
import time
import file_pb2
import file_pb2_grpc
class FileTransferServicer(file_pb2_grpc.FileTransferServicer):
def DownloadFile(self, request, context):
file_name = request.file_name
file_path = request.file_path
try:
# read the file
print('Prepare to download file: ' + file_path + file_name)
with open(file_path + file_name, 'rb') as file:
file_content = file.read()
# response
response = file_pb2.FileResponse(file_content=file_content)
return response
except FileNotFoundError:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details('The file is not exist: ' + file_path + file_name)
return file_pb2.FileResponse()
def serve():
# make gRPC server
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
# Add server implementation to gRPC server
file_pb2_grpc.add_FileTransferServicer_to_server(FileTransferServicer(), server)
# open the port
server.add_insecure_port('[::]:50051')
server.start()
print('The server is running, waiting for connect...')
try:
# Blocking threads, continuously providing services
while True:
time.sleep(86400)
except KeyboardInterrupt:
# close the server
server.stop(0)
if __name__ == '__main__':
serve()
class FileTransferServicer 具体实现服务类,其中 DownloadFile 具体实现下载文件方法。
def serve() 实现 server 的实例化,包括创建 server,添加 gRPC 注册,为服务指定端口。
三、编写 client.py 实现服务测试
在实现服务端服务方法后,client.py 负责测试服务端功能(也可使用 grpcurl 等工具进行客户侧测试)。
创建 client.py 文件,示例代码:
import grpc
import file_pb2
import file_pb2_grpc
def download_file_from_server(file_path, file_name, file_save_to_path):
# make channel connecting
channel = grpc.insecure_channel('localhost:50051', options=[
('grpc.max_receive_message_length', 2000 * 1024 * 1024) # the max size is 2000MB
])
# make stub
stub = file_pb2_grpc.FileTransferStub(channel)
# make request
request = file_pb2.FileRequest(file_name=file_name, file_path=file_path)
try:
# call the DownloadFile method
response = stub.DownloadFile(request)
file_content = response.file_content
file_save_to_path = file_save_to_path
# save the file to local dir
with open(file_save_to_path + file_name, 'wb') as file:
file.write(file_content)
print('Succeed')
except grpc.RpcError as e:
print('Failed: %s' % e.details())
if __name__ == '__main__':
file_path = 'C:\\file_service/downloadfrom/' # the file path of server
file_name = 'test.txt' # the file name
file_save_to_path = 'C:\\file_service/downloadto/' # the local save file path
download_file_from_server(file_path, file_name, file_save_to_path)
此 client 端测试代码实现将 test.txt 文件从 server 端(downloadfrom 目录)下载到 client 端(downloadto 目录),在具体实践中 server 与 client 可能不在同一台主机上,可自行指定目录。
客户端的实例包括创建存根 stub,初始化传入数据 request 及针对服务端提供的端口调用方法,若客户端与服务器不在同一主机,还需自行指定域名或 IP 地址。
四、运行 server.py 及 client.py
分别在不同的终端执行以下命令:
在 server.py 目录下,运行:
python server.py
此时服务端会在指定端口(本文为 50051)上执行监听,显示:
The server is running, waiting for connect...
在 client.py 目录下,运行:
python client.py
客户端将对服务端提供的端口发送下载文件请求,而后将指定文件下载至指定目录下。
此时服务端显示:
Prepare to download file: C:\file_service/downloadfrom/test.txt
下载成功,客户端显示:
Succeed
文件成功从服务端(downloadfrom 目录)下载至(downloadto 目录)。