In grpc-mate we have product_read_servicer.py to show how to output large binary stream via data chunk in grpc.
protobuf
rpc DownloadProductImage(DownloadProductImageRequest) returns(stream DataChunk){
}
message DownloadProductImageRequest {
int64 product_id = 1;
}
message DataChunk {
bytes data = 1;
}
Server Side
def DownloadProductImage(self, request, context):
chunk_size = 1024
image_path = Path(__file__).resolve().parent.parent.joinpath('images/python-grpc.png')
with image_path.open('rb') as f:
while True:
chunk = f.read(chunk_size)
if not chunk:
break
yield DataChunk(data=chunk)
- it's recommended to use pathlib over os.path as it's follow the OO design, see details from this article
- thanks to python's simple design, it's much simpler than the Java Version to stream binary files as stream, we just need to open the binary file in binary mode and read the data chunk by chunk, then just yield it, the grpc framework will take care all the other stuff
Client Side
def test_DownloadProductImage(grpc_stub):
faker = Faker()
target_image_file = faker.file_name(category=None, extension='png')
data_chunks = grpc_stub.DownloadProductImage(DownloadProductImageRequest(product_id=1))
with open(target_image_file, 'wb') as f:
for chunk in data_chunks:
f.write(chunk.data)
original_image_file = Path(__file__).resolve().parent.parent.parent.joinpath('images/python-grpc.png')
assert filecmp.cmp(original_image_file, target_image_file)
os.remove(target_image_file)
- first we create a fake image file path to save download image by python faker
- then call the grpc service, it will return an iterator
- next we could open the fake image file in write mode and iterator over the data chunk to save the image filee
- after the fake image file is saved locally , we could use
filecmp
module to make sure the downloaded file is the same as original image file - finally we could delete the temp fake image file as a clean up.