関連
https://www.notion.so/Flutter-x-golang-x-gRPC-f299ddde01bf48bc87ed07d2f4396061
アップロードよりも単純。gRPC ばんざい
schema
service PlayerService {
  rpc Download (DownloadRequest) returns (stream DownloadResponse) {}
}
message DownloadRequest {
  int64 audio_id = 1;
}
message DownloadResponse {
  util.File file = 1;
}
server
func (a *PlayerAdapter) Download(r *playerpb.DownloadRequest, stream playerpb.PlayerService_DownloadServer) error {
	ctx := stream.Context()
	audio, err := a.audioUC.Get(ctx, r.AudioId)
	if err != nil {
		return err
	}
	f, err := os.Open(audio.Path)
	if err != nil {
		return err
	}
	defer f.Close()
  // バイト単位で分割してストリーミング(要調整)
	var b [4096 * 1000]byte
	for {
		n, err := f.Read(b[:])
		if err != nil {
			if err != io.EOF {
				return err
			}
			break
		}
		err = stream.Send(&playerpb.DownloadResponse{
			File: &utilpb.File{
				Content: b[:n],
			},
		})
		if err != nil {
			return err
		}
	}
	util.LoggingInfo(ctx, "PlayerAdapter.Download end", logrus.Fields{
		"r.AudioId": r.AudioId,
	})
	return nil
}
client
@override
  Future<Uint8List> download(int id) async {
    final channel = createChannel();
    final stub = _createStub(channel);
    final request = $pbgrpc.DownloadRequest()
      ..audioId = $fixnum.Int64(id);
    var results = <int>[];
    // 結果を全て待つ場合, await for で
    await for (final reply in stub.download(request)) {
      results = [...results, ...reply.file.content];
    }
    return Uint8List.fromList(results);
  }