diff --git a/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs b/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs index 73e982a..19c65d8 100644 --- a/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs +++ b/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs @@ -4,14 +4,17 @@ using System.Threading.Tasks; using Grpc.Core; using FrostFS.Object; +using FrostFS.SDK.ModelsV2; namespace FrostFS.SDK.ClientV2; -internal class ObjectReader(AsyncServerStreamingCall call) : IDisposable +public class ObjectReader(AsyncServerStreamingCall call) : IObjectReader { + private bool disposed = false; + public AsyncServerStreamingCall Call { get; private set; } = call; - public async Task ReadHeader() + internal async Task ReadHeader() { if (!await Call.ResponseStream.MoveNext()) throw new InvalidOperationException("unexpected end of stream"); @@ -45,6 +48,12 @@ internal class ObjectReader(AsyncServerStreamingCall call) : IDispo public void Dispose() { - Call.Dispose(); + if (!disposed) + { + Call.Dispose(); + GC.SuppressFinalize(this); + + disposed = true; + } } } diff --git a/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs index 8619cdd..bf35ad7 100644 --- a/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs +++ b/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs @@ -80,7 +80,7 @@ internal class ObjectServiceProvider : ContextAccessor var obj = await GetObject(request, ctx); - return obj.ToModel(); + return obj; } internal Task PutObjectAsync(PutObjectParameters parameters, Context ctx) @@ -311,27 +311,38 @@ internal class ObjectServiceProvider : ContextAccessor } // TODO: add implementation with stream writer! - private async Task GetObject(GetRequest request, Context ctx) + private async Task GetObject(GetRequest request, Context ctx) { - using var stream = GetObjectInit(request, ctx); + var reader = GetObjectInit(request, ctx); - var obj = await stream.ReadHeader(); - var payload = new byte[obj.Header.PayloadLength]; - var offset = 0L; - var chunk = await stream.ReadChunk(); + var obj = await reader.ReadHeader(); - while (chunk is not null && (ulong)offset < obj.Header.PayloadLength) - { - var length = Math.Min((long)obj.Header.PayloadLength - offset, chunk.Length); + var @object = obj.ToModel(); - Array.Copy(chunk, 0, payload, offset, length); - offset += chunk.Length; - chunk = await stream.ReadChunk(); - } + @object.ObjectReader = reader; - obj.Payload = ByteString.CopyFrom(payload); + return @object; + + // obj. - return obj; + // return obj.ToModel(); + + // var payload = new byte[obj.Header.PayloadLength]; + // var offset = 0L; + // var chunk = await stream.ReadChunk(); + + // while (chunk is not null && (ulong)offset < obj.Header.PayloadLength) + // { + // var length = Math.Min((long)obj.Header.PayloadLength - offset, chunk.Length); + + // Array.Copy(chunk, 0, payload, offset, length); + // offset += chunk.Length; + // chunk = await stream.ReadChunk(); + // } + + // obj.Payload = ByteString.CopyFrom(payload); + + // return obj; } private ObjectReader GetObjectInit(GetRequest initRequest, Context ctx) diff --git a/src/FrostFS.SDK.ModelsV2/Object/Object.cs b/src/FrostFS.SDK.ModelsV2/Object/Object.cs index 51c4630..e129601 100644 --- a/src/FrostFS.SDK.ModelsV2/Object/Object.cs +++ b/src/FrostFS.SDK.ModelsV2/Object/Object.cs @@ -27,6 +27,8 @@ public class Object } public byte[] Payload { get; set; } + + public IObjectReader? ObjectReader { get; set; } public Signature? Signature { get; set; } diff --git a/src/FrostFS.SDK.ModelsV2/Object/ObjectAttribute.cs b/src/FrostFS.SDK.ModelsV2/Object/ObjectAttribute.cs index b114f2b..92d38b2 100644 --- a/src/FrostFS.SDK.ModelsV2/Object/ObjectAttribute.cs +++ b/src/FrostFS.SDK.ModelsV2/Object/ObjectAttribute.cs @@ -1,13 +1,7 @@ namespace FrostFS.SDK.ModelsV2; -public class ObjectAttribute +public class ObjectAttribute(string key, string value) { - public string Key { get; set; } - public string Value { get; set; } - - public ObjectAttribute(string key, string value) - { - Key = key; - Value = value; - } + public string Key { get; set; } = key; + public string Value { get; set; } = value; } diff --git a/src/FrostFS.SDK.Tests/ClientTestLive.cs b/src/FrostFS.SDK.Tests/ClientTestLive.cs index 70dda66..f596472 100644 --- a/src/FrostFS.SDK.Tests/ClientTestLive.cs +++ b/src/FrostFS.SDK.Tests/ClientTestLive.cs @@ -1,3 +1,4 @@ +using System.Security.Cryptography; using FrostFS.SDK.ClientV2; using FrostFS.SDK.ClientV2.Interfaces; using FrostFS.SDK.ModelsV2; @@ -82,13 +83,17 @@ public class ClientTestLive Assert.NotNull(container); + Random rnd = new(); + var bytes = new byte[6*1024*1024 + 100]; + rnd.NextBytes(bytes); + var param = new PutObjectParameters { Header = new ObjectHeader( containerId: containerId, type: ObjectType.Regular, new ObjectAttribute("fileName", "test")), - Payload = new MemoryStream([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]), + Payload = new MemoryStream(bytes), ClientCut = false }; @@ -102,7 +107,7 @@ public class ClientTestLive hasObject = true; var objHeader = await fsClient.GetObjectHeadAsync(containerId, objectId); - Assert.Equal(10u, objHeader.PayloadLength); + Assert.Equal((ulong)bytes.Length, objHeader.PayloadLength); Assert.Single(objHeader.Attributes); Assert.Equal("fileName", objHeader.Attributes.First().Key); Assert.Equal("test", objHeader.Attributes.First().Value); @@ -112,7 +117,16 @@ public class ClientTestLive var @object = await fsClient.GetObjectAsync(containerId, objectId!); - Assert.Equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], @object.Payload); + var downloadedBytes = new byte[@object.Header.PayloadLength]; + MemoryStream ms = new(downloadedBytes); + + byte[]? chunk = null; + while ((chunk = await @object.ObjectReader.ReadChunk()) != null) + { + ms.Write(chunk); + } + + Assert.Equal(MD5.HashData(bytes), MD5.HashData(downloadedBytes)); await Cleanup(fsClient); @@ -145,13 +159,17 @@ public class ClientTestLive Assert.NotNull(container); + Random rnd = new(); + var bytes = new byte[6*1024*1024 + 100]; + rnd.NextBytes(bytes); + var param = new PutObjectParameters { Header = new ObjectHeader( containerId: containerId, type: ObjectType.Regular, new ObjectAttribute("fileName", "test")), - Payload = new MemoryStream([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]), + Payload = new MemoryStream(bytes), ClientCut = true }; @@ -165,7 +183,7 @@ public class ClientTestLive hasObject = true; var objHeader = await fsClient.GetObjectHeadAsync(containerId, objectId); - Assert.Equal(10u, objHeader.PayloadLength); + Assert.Equal((ulong)bytes.Length, objHeader.PayloadLength); Assert.Single(objHeader.Attributes); Assert.Equal("fileName", objHeader.Attributes.First().Key); Assert.Equal("test", objHeader.Attributes.First().Value); @@ -175,7 +193,14 @@ public class ClientTestLive var @object = await fsClient.GetObjectAsync(containerId, objectId!); - Assert.Equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], @object.Payload); + var downloadedBytes = new byte[@object.Header.PayloadLength]; + MemoryStream ms = new(downloadedBytes); + + byte[]? chunk = null; + while ((chunk = await @object.ObjectReader.ReadChunk()) != null) + { + ms.Write(chunk); + } await Cleanup(fsClient);