Skip to content

Commit 402878f

Browse files
committed
Fix API
1 parent 3f367fb commit 402878f

16 files changed

Lines changed: 664 additions & 401 deletions

README.md

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
Привет! Это обертка над HttpClient для работы с S3 хранилищами. Мотивация создания была простейшей - я не понимал,
1010
почему клиенты [AWS](https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/welcome.html)
11-
и [Minio](https://github.com/minio/minio-dotnet) едят так много памяти . Результат моих экспериментов: скорость
11+
и [Minio](https://github.com/minio/minio-dotnet) едят так много памяти. Результат моих экспериментов: скорость
1212
почти как у Minio, а памяти потребляю почти в 200 раз меньше, чем клиент для AWS.
1313

1414
```ini
@@ -18,13 +18,13 @@ Unknown processor
1818
[Host] : .NET 8.0.4 (8.0.424.16909), Arm64 RyuJIT AdvSIMD
1919
.NET 8.0 : .NET 8.0.4 (8.0.424.16909), Arm64 RyuJIT AdvSIMD
2020

21-
Job=.NET 8.0 Runtime=.NET 8.0
21+
Job=.NET 8.0 Runtime=.NET 8.0
2222
```
23-
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio |
24-
|-------- |--------:|---------:|---------:|------:|--------:|-----------:|----------:|-------------:|------------:|
25-
| Aws | 1.51 s | 0.030 s | 0.035 s | 1.42 | 0.03 | 80000.00 | 5000.00 | 201710 KB | 420.92 |
26-
| Minio | 1.48 s | 0.027 s | 0.023 s | 1.39 | 0.02 | - | - | 279524 KB | 583.30 |
27-
| Storage | 1.06 s | 0.014 s | 0.013 s | 1.00 | 0.00 | - | - | 479 KB | 1.00 |
23+
| Method | Mean | Ratio | Gen0 | Gen1 | Allocated | Alloc Ratio |
24+
|-------- |--------:|------:|------------:|-----------:|--------------:|------------:|
25+
| Aws | 1.497 s | 1.45 | 80 000 | 6 000 | 201 728.07 KB | 333.24 |
26+
| Minio | 1.468 s | 1.43 | - | - | 279 532.97 KB | 461.76 |
27+
| Storage | 1.031 s | 1.00 | - | - | 605.36 KB | 1.00 |
2828

2929
## Создание клиента
3030

@@ -57,7 +57,7 @@ Amazon S3 не тестировался.
5757
bool bucketCreateResult = await storageClient.CreateBucket(cancellationToken);
5858
Console.WriteLine(bucketCreateResult
5959
? "Bucket создан"
60-
: $"Bucket не был создан");
60+
: "Bucket не был создан");
6161
```
6262

6363
### Проверка существования bucket'a
@@ -100,8 +100,8 @@ if (fileUploadResult) Console.WriteLine("Файл загружен");
100100

101101
using S3Upload upload = await storageClient.UploadFile(fileName, fileType, cancellationToken);
102102

103-
await upload.Upload(stream, cancellationToken); // загружаем часть документа
104-
if (!await upload.Upload(byteArray, cancellationToken)) { // загружаем другую часть документа
103+
await upload.AddParts(stream, cancellationToken); // загружаем части документа
104+
if (!await upload.AddParts(byteArray, cancellationToken)) { // загружаем другую часть документа
105105
await upload.Abort(cancellationToken); // отменяем загрузку
106106
}
107107
else {
@@ -125,11 +125,21 @@ else {
125125
}
126126
```
127127

128+
### Получение файла как Stream
129+
130+
```csharp
131+
var fileStream = await storageClient.GetFileStream(fileName, cancellationToken);
132+
```
133+
134+
В случае, если файл не существует, возвратится `Stream.Null`.
135+
128136
### Проверка существования файла
129137

130138
```csharp
131139
bool fileExistsResult = await storageClient.IsFileExists(fileName, cancellationToken);
132-
if (fileExistsResult) Console.WriteLine("Файл существует");
140+
if (fileExistsResult) {
141+
Console.WriteLine("Файл существует");
142+
}
133143
```
134144

135145
### Создание подписанной ссылки на файл
@@ -139,7 +149,9 @@ if (fileExistsResult) Console.WriteLine("Файл существует");
139149

140150
```csharp
141151
string? preSignedFileUrl = storageClient.GetFileUrl(fileName, expiration);
142-
if (preSignedFileUrl != null) Console.WriteLine($"URL получен: {preSignedFileUrl}");
152+
if (preSignedFileUrl != null) {
153+
Console.WriteLine($"URL получен: {preSignedFileUrl}");
154+
}
143155
```
144156

145157
Существует не безопасный способ создать ссылку, без проверки наличия файла в S3.

src/Storage.Benchmark/InternalBenchmarks/DownloadBenchmark.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public void Config()
1818
var config = BenchmarkHelper.ReadConfiguration();
1919
var settings = BenchmarkHelper.ReadSettings(config);
2020

21-
_cancellation = new CancellationToken();
21+
_cancellation = CancellationToken.None;
2222
_fileId = "привет-как-делаdcd156a8-b6bd-4130-a2c7-8a38dbfebbc7";
2323
_s3Client = BenchmarkHelper.CreateStoragesClient(settings);
2424

src/Storage.Benchmark/Storage.Benchmark.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.1" />
2222
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
2323
<PackageReference Include="Minio" Version="6.0.2" />
24-
<PackageReference Include="AWSSDK.S3" Version="3.7.307.23" />
24+
<PackageReference Include="AWSSDK.S3" Version="3.7.307.24" />
2525
</ItemGroup>
2626

2727

src/Storage.Tests/ObjectShould.cs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,23 @@ public ObjectShould(StorageFixture fixture)
1919
_notExistsBucketClient = TestHelper.CloneClient(_fixture);
2020
}
2121

22+
[Fact]
23+
public async Task AbortMultipart()
24+
{
25+
var fileName = _fixture.Create<string>();
26+
var data = GetByteArray(50 * 1024 * 1024);
27+
28+
using var uploader = await _client.UploadFile(fileName, StreamContentType, _ct);
29+
30+
var addResult = await uploader.AddPart(data, _ct);
31+
addResult
32+
.Should().BeTrue();
33+
34+
var abortResult = await uploader.Abort(_ct);
35+
abortResult
36+
.Should().BeTrue();
37+
}
38+
2239
[Fact]
2340
public async Task AllowParallelUploadMultipleFiles()
2441
{
@@ -121,12 +138,26 @@ public async Task DisposeStorageFile()
121138
var fileName = await CreateTestFile();
122139
using var fileGetResult = await _client.GetFile(fileName, _ct);
123140

124-
// ReSharper disable once MethodHasAsyncOverload
141+
// ReSharper disable once DisposeOnUsingVariable
125142
fileGetResult.Dispose();
126143

127144
await DeleteTestFile(fileName);
128145
}
129146

147+
[Fact]
148+
public async Task GetFileStream()
149+
{
150+
var fileName = await CreateTestFile();
151+
152+
var fileStream = await _client.GetFileStream(fileName, _ct);
153+
154+
using var bufferStream = GetEmptyByteStream();
155+
await fileStream.CopyToAsync(bufferStream, _ct);
156+
157+
await EnsureFileSame(fileName, bufferStream.ToArray());
158+
await DeleteTestFile(fileName);
159+
}
160+
130161
[Fact]
131162
public async Task GetFileUrl()
132163
{
@@ -261,6 +292,20 @@ public async Task PutByteArray()
261292
await DeleteTestFile(fileName);
262293
}
263294

295+
[Fact]
296+
public async Task PutBigByteArray()
297+
{
298+
var fileName = _fixture.Create<string>();
299+
var data = GetByteArray(50 * 1024 * 1024);
300+
var filePutResult = await _client.UploadFile(fileName, StreamContentType, data, _ct);
301+
302+
filePutResult
303+
.Should().BeTrue();
304+
305+
await EnsureFileSame(fileName, data);
306+
await DeleteTestFile(fileName);
307+
}
308+
264309
[Fact]
265310
public async Task PutStream()
266311
{
@@ -275,6 +320,20 @@ public async Task PutStream()
275320
await DeleteTestFile(fileName);
276321
}
277322

323+
[Fact]
324+
public async Task PutBigStream()
325+
{
326+
var fileName = _fixture.Create<string>();
327+
var data = GetByteStream(50 * 1024 * 1024);
328+
var filePutResult = await _client.UploadFile(fileName, StreamContentType, data, _ct);
329+
330+
filePutResult
331+
.Should().BeTrue();
332+
333+
await EnsureFileSame(fileName, data);
334+
await DeleteTestFile(fileName);
335+
}
336+
278337
[Fact]
279338
public async Task Upload()
280339
{

src/Storage.Tests/StorageFixture.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public static MemoryStream GetByteStream(int size = DefaultByteArraySize)
4848
return new MemoryStream(GetByteArray(size));
4949
}
5050

51-
public static MemoryStream GetEmptyByteStream(long? size)
51+
public static MemoryStream GetEmptyByteStream(long? size = null)
5252
{
5353
return size.HasValue
5454
? new MemoryStream(new byte[(int)size])

src/Storage/S3Client.Buckets.cs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using Storage.Utils;
2+
using static Storage.Utils.HashHelper;
3+
4+
namespace Storage;
5+
6+
/// <summary>
7+
/// Функции управления бакетом
8+
/// </summary>
9+
public sealed partial class S3Client
10+
{
11+
public async Task<bool> CreateBucket(CancellationToken ct)
12+
{
13+
HttpResponseMessage response;
14+
using (var request = CreateRequest(HttpMethod.Put))
15+
{
16+
response = await Send(request, EmptyPayloadHash, ct).ConfigureAwait(false);
17+
}
18+
19+
switch (response.StatusCode)
20+
{
21+
case HttpStatusCode.OK:
22+
response.Dispose();
23+
return true;
24+
case HttpStatusCode.Conflict: // already exists
25+
response.Dispose();
26+
return false;
27+
default:
28+
Errors.UnexpectedResult(response);
29+
return false;
30+
}
31+
}
32+
33+
public async Task<bool> DeleteBucket(CancellationToken ct)
34+
{
35+
HttpResponseMessage response;
36+
using (var request = CreateRequest(HttpMethod.Delete))
37+
{
38+
response = await Send(request, EmptyPayloadHash, ct).ConfigureAwait(false);
39+
}
40+
41+
switch (response.StatusCode)
42+
{
43+
case HttpStatusCode.NoContent:
44+
response.Dispose();
45+
return true;
46+
case HttpStatusCode.NotFound:
47+
response.Dispose();
48+
return false;
49+
default:
50+
Errors.UnexpectedResult(response);
51+
return false;
52+
}
53+
}
54+
55+
public async Task<bool> IsBucketExists(CancellationToken ct)
56+
{
57+
HttpResponseMessage response;
58+
using (var request = CreateRequest(HttpMethod.Head))
59+
{
60+
response = await Send(request, EmptyPayloadHash, ct).ConfigureAwait(false);
61+
}
62+
63+
switch (response.StatusCode)
64+
{
65+
case HttpStatusCode.OK:
66+
response.Dispose();
67+
return true;
68+
case HttpStatusCode.NotFound:
69+
response.Dispose();
70+
return false;
71+
default:
72+
Errors.UnexpectedResult(response);
73+
return false;
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)