.net core gRPC 客户端流式上传图片,文件。gRPC文件分段传输
电脑版发表于:2020/2/9 14:02
html前台,完全一样:
<form method="post" action="/home/UpLoadImgStream" enctype="multipart/form-data"> <input type="file" name="upfile" /> <input type="submit" name="上传" /> </form>
gRPC服务端
proto
syntax = "proto3"; option csharp_namespace = "GrpcService1"; package UpImgStream; // The greeting service definition. service UpImgStream { // Sends a greeting rpc DoUpLoad (stream StreamRequest) returns (UpImgStreamReply); } // The request message containing the user's name. message StreamRequest { bytes content = 1; //文件后缀名(弃用,通过另外的方式在文件之前传递) string fileExt=2; } // The response message containing the greetings. message UpImgStreamReply { string message = 1; }
实现:
public override async Task<UpImgStreamReply> DoUpLoad(IAsyncStreamReader<StreamRequest> requestStream, ServerCallContext context) { try { //获取调用方的id string host = context.Host; //if (context.UserState.ContainsKey("fileExt")) //{ // object fileExt = context.UserState["fileExt"]; //} //注意获取的时候用小写 var fileExtMetadata = context.RequestHeaders.FirstOrDefault(a => a.Key == "fileext"); if (fileExtMetadata == null) { return new UpImgStreamReply { Message = "上传失败!" }; } string fileExt = fileExtMetadata.Value; // 新建一个文件流用于存放我们获取到数据 using (var fs = new FileStream("c://" + Guid.NewGuid().ToString().Replace("-", "") + "." + fileExt, FileMode.Create)) { while (await requestStream.MoveNext()) // 循环从流中读取数据(监听gRPC客户端的流写入) { requestStream.Current.Content.WriteTo(fs); // 将数据写入到文件流中 } //数据是先被读到了内存中,再把数据写到文件中,当你数据读完的时候不代表你的数据已经写完了,因为还有一部分有可能会留在内存这个缓冲区中。这时候如果你调用了close()方法关闭了读写流,那么这部分数据就会丢失,所以应该在关闭读写流之前先flush()。 fs.Flush(); } return new UpImgStreamReply { Message = "上传成功!" }; } catch (Exception ex) { return new UpImgStreamReply { Message = "上传失败!" }; } }
gRPC客户端
方法一:文件分段传输
适用于文件有点点大的情况
/// <summary> /// 调用gRPC 客户端流式图片上传(文件分段传输) /// </summary> public async void UpLoadImgStream(List<IFormFile> upfile) { //获取页面传递的文件流 foreach (var formFile in upfile) { if (formFile.Length > 0) { #region 获取gRPC接口 AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); var channel = GrpcChannel.ForAddress("http://localhost:8099/"); var client = new UpImgStream.UpImgStreamClient(channel); #endregion //获取文件的后缀名 string fileExt = formFile.FileName.Substring(formFile.FileName.LastIndexOf('.') + 1); //文件扩展名 Metadata entries = new Metadata(); entries.Add("fileExt", fileExt); AsyncClientStreamingCall<StreamRequest, UpImgStreamReply> call = client.DoUpLoad(entries);//先调用服务后面在提供数据 //得到文件流 Stream stream = formFile.OpenReadStream(); try { var remainingLength = stream.Length; //文件总长度 int sendlength = 100 * 1024;//1048576; //先把分段读取的数据存储在集合里边,因为直接调用gRPC传输,第二次读取流的时候会关闭,暂时还没有解决就使用这种方法 List<byte[]> byteList = new List<byte[]>(); byte[] buff = new byte[sendlength]; // 缓冲区,这里我们设置为 nkb while (remainingLength > 0) // 若未读完则继续读取(把文件分为单位nkb的传递,如果小于nkb则一次传递完) { if (remainingLength < sendlength)//剩下的可以一次传递完 { buff = new byte[remainingLength]; } //注意读一次后调用WriteAsync,Stream流就关闭了,但是如果不执行WriteAsync是可以读完所有的 int len = stream.Read(buff, 0, buff.Length); // 异步从文件中读取数据到缓冲区中 remainingLength -= len; // 剩余长度=当前总长度-这次实际读取的长度 byteList.Add(buff); } //分段调用gRPC流进行文件传输 foreach (byte[] item in byteList) { //向gRPC流中写入我们刚刚读取的数据 await call.RequestStream.WriteAsync(new StreamRequest() { Content = ByteString.CopyFrom(item) }); } await call.RequestStream.CompleteAsync();//完成关闭客户端流 } catch (Exception ex) { } } } }
方法二:一次性传输
/// <summary> /// 调用gRPC 客户端流式图片上传(一次性传输) /// </summary> public async void UpLoadImgStream(List<IFormFile> upfile) { //获取页面传递的文件流 foreach (var formFile in upfile) { if (formFile.Length > 0) { #region 获取gRPC接口 AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); var channel = GrpcChannel.ForAddress("http://localhost:8099/"); var client = new UpImgStream.UpImgStreamClient(channel); #endregion //获取文件的后缀名 string fileExt = formFile.FileName.Substring(formFile.FileName.LastIndexOf('.') + 1); //文件扩展名 Metadata entries = new Metadata(); entries.Add("fileExt", fileExt); AsyncClientStreamingCall<StreamRequest, UpImgStreamReply> call = client.DoUpLoad(entries);//先调用服务后面在提供数据 //得到文件流 Stream stream = formFile.OpenReadStream(); try { //方法1:一次传递完 StreamRequest streamRequest = new StreamRequest() { Content = ByteString.FromStream(stream) }; await call.RequestStream.WriteAsync(streamRequest); await call.RequestStream.CompleteAsync();//完成关闭客户端流 } catch (Exception ex) { } } } }
得到流式调用的返回值:
await call.RequestStream.CompleteAsync();//完成关闭客户端流(注意一定要调用不然整个调用过程完成不了) //得到返回值 UpImgStreamReply upImgStreamReply = await call.ResponseAsync;
下载地址:http://www.tnblog.net/resource/show/aojiancc2/af3134fbbe174025ad34e7ccf7de465f