Skip to content

Commit 8fa8389

Browse files
authored
Add uploader sample (#1529)
1 parent bce5852 commit 8fa8389

File tree

12 files changed

+345
-0
lines changed

12 files changed

+345
-0
lines changed

examples/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,3 +277,12 @@ The container example shows how to create a gRPC Kubernetes app. There are two c
277277

278278
* [Kubernetes](https://kubernetes.io/)
279279
* Configure [gRPC client-side load balancing](https://docs.microsoft.com/aspnet/core/grpc/loadbalancing)
280+
281+
## [Uploader](./Uploader)
282+
283+
The uploader shows how to upload a file in chunks using a client streaming gRPC method.
284+
285+
##### Scenarios:
286+
287+
* Client streaming call
288+
* Binary payload
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net6.0</TargetFramework>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<Protobuf Include="..\Proto\upload.proto" GrpcServices="Client" Link="Protos\upload.proto" />
10+
11+
<PackageReference Include="Google.Protobuf" Version="$(GoogleProtobufPackageVersion)" />
12+
<PackageReference Include="Grpc.Net.Client" Version="$(GrpcDotNetPackageVersion)" />
13+
<PackageReference Include="Grpc.Tools" Version="$(GrpcPackageVersion)" PrivateAssets="All" />
14+
</ItemGroup>
15+
</Project>
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#region Copyright notice and license
2+
3+
// Copyright 2019 The gRPC Authors
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
#endregion
18+
19+
using Google.Protobuf;
20+
using Grpc.Net.Client;
21+
using Upload;
22+
23+
namespace Client
24+
{
25+
public class Program
26+
{
27+
private const int ChunkSize = 1024 * 32; // 32 KB
28+
29+
static async Task Main(string[] args)
30+
{
31+
using var channel = GrpcChannel.ForAddress("https://localhost:5001");
32+
var client = new Uploader.UploaderClient(channel);
33+
34+
Console.WriteLine("Starting call");
35+
var call = client.UploadFile();
36+
37+
Console.WriteLine("Sending file metadata");
38+
await call.RequestStream.WriteAsync(new UploadFileRequest
39+
{
40+
Metadata = new FileMetadata
41+
{
42+
FileName = "pancakes.jpg"
43+
}
44+
});
45+
46+
var buffer = new byte[ChunkSize];
47+
await using var readStream = File.OpenRead("pancakes.jpg");
48+
49+
while (true)
50+
{
51+
var count = await readStream.ReadAsync(buffer);
52+
if (count == 0)
53+
{
54+
break;
55+
}
56+
57+
Console.WriteLine("Sending file data chunk of length " + count);
58+
await call.RequestStream.WriteAsync(new UploadFileRequest
59+
{
60+
Data = UnsafeByteOperations.UnsafeWrap(buffer.AsMemory(0, count))
61+
});
62+
}
63+
64+
Console.WriteLine("Complete request");
65+
await call.RequestStream.CompleteAsync();
66+
67+
var response = await call;
68+
Console.WriteLine("Upload id: " + response.Id);
69+
70+
Console.WriteLine("Shutting down");
71+
Console.WriteLine("Press any key to exit...");
72+
Console.ReadKey();
73+
}
74+
}
75+
}
105 KB
Loading
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2019 The gRPC Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
syntax = "proto3";
16+
17+
package upload;
18+
19+
service Uploader {
20+
rpc UploadFile (stream UploadFileRequest) returns (UploadFileResponse);
21+
}
22+
23+
message UploadFileRequest {
24+
FileMetadata metadata = 1;
25+
bytes data = 2;
26+
}
27+
28+
message FileMetadata {
29+
string file_name = 1;
30+
}
31+
32+
message UploadFileResponse {
33+
string id = 1;
34+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#region Copyright notice and license
2+
3+
// Copyright 2019 The gRPC Authors
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
#endregion
18+
19+
using Microsoft.AspNetCore.Hosting;
20+
using Microsoft.Extensions.Hosting;
21+
22+
namespace Server
23+
{
24+
public class Program
25+
{
26+
public static void Main(string[] args)
27+
{
28+
CreateHostBuilder(args).Build().Run();
29+
}
30+
31+
public static IHostBuilder CreateHostBuilder(string[] args) =>
32+
Host.CreateDefaultBuilder(args)
33+
.ConfigureWebHostDefaults(webBuilder =>
34+
{
35+
webBuilder.UseStartup<Startup>();
36+
});
37+
}
38+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net6.0</TargetFramework>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<Protobuf Include="..\Proto\upload.proto" GrpcServices="Server" Link="Protos\upload.proto" />
9+
10+
<PackageReference Include="Grpc.AspNetCore" Version="$(GrpcDotNetPackageVersion)" />
11+
</ItemGroup>
12+
13+
</Project>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#region Copyright notice and license
2+
3+
// Copyright 2019 The gRPC Authors
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
#endregion
18+
19+
using Grpc.Core;
20+
using Upload;
21+
22+
namespace Server
23+
{
24+
public class UploaderService : Uploader.UploaderBase
25+
{
26+
private readonly ILogger _logger;
27+
private readonly IConfiguration _config;
28+
29+
public UploaderService(ILoggerFactory loggerFactory, IConfiguration config)
30+
{
31+
_logger = loggerFactory.CreateLogger<UploaderService>();
32+
_config = config;
33+
}
34+
35+
public override async Task<UploadFileResponse> UploadFile(IAsyncStreamReader<UploadFileRequest> requestStream, ServerCallContext context)
36+
{
37+
var uploadId = Path.GetRandomFileName();
38+
var uploadPath = Path.Combine(_config["StoredFilesPath"], uploadId);
39+
Directory.CreateDirectory(uploadPath);
40+
41+
await using var writeStream = File.Create(Path.Combine(uploadPath, "data.bin"));
42+
43+
await foreach (var message in requestStream.ReadAllAsync())
44+
{
45+
if (message.Metadata != null)
46+
{
47+
await File.WriteAllTextAsync(Path.Combine(uploadPath, "metadata.json"), message.Metadata.ToString());
48+
}
49+
if (message.Data != null)
50+
{
51+
await writeStream.WriteAsync(message.Data.Memory);
52+
}
53+
}
54+
55+
return new UploadFileResponse { Id = uploadId };
56+
}
57+
}
58+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#region Copyright notice and license
2+
3+
// Copyright 2019 The gRPC Authors
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
#endregion
18+
19+
using Microsoft.AspNetCore.Builder;
20+
using Microsoft.AspNetCore.Hosting;
21+
using Microsoft.Extensions.DependencyInjection;
22+
using Microsoft.Extensions.Hosting;
23+
24+
namespace Server
25+
{
26+
public class Startup
27+
{
28+
public void ConfigureServices(IServiceCollection services)
29+
{
30+
services.AddGrpc();
31+
}
32+
33+
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
34+
{
35+
if (env.IsDevelopment())
36+
{
37+
app.UseDeveloperExceptionPage();
38+
}
39+
40+
app.UseRouting();
41+
42+
app.UseEndpoints(endpoints =>
43+
{
44+
endpoints.MapGrpcService<UploaderService>();
45+
});
46+
}
47+
}
48+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Debug",
5+
"System": "Information",
6+
"Grpc": "Information",
7+
"Microsoft": "Information"
8+
}
9+
}
10+
}

0 commit comments

Comments
 (0)