Skip to content

Commit 4da619b

Browse files
authored
Merge pull request #7 from mwilde/support_checksum_algorithms
Enable support for checksum calculation
2 parents d4633a5 + 224f51f commit 4da619b

File tree

6 files changed

+130
-47
lines changed

6 files changed

+130
-47
lines changed

Examples/DecompressGzAndUpload/DecompressGzAndUpload.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<ItemGroup>
88
<PackageReference Include="Amazon.Lambda.Core" Version="2.5.0" />
99
<PackageReference Include="Amazon.Lambda.Serialization.SystemTextJson" Version="2.4.4" />
10-
<PackageReference Include="AWSSDK.S3" Version="3.7.410.1" />
10+
<PackageReference Include="AWSSDK.S3" Version="3.7.410.3" />
1111
<PackageReference Include="System.Text.Json" Version="9.0.0" />
1212
</ItemGroup>
1313
<ItemGroup>

Examples/DecryptAesAndUpload/DecryptAesAndUpload.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<ItemGroup>
88
<PackageReference Include="Amazon.Lambda.Core" Version="2.5.0" />
99
<PackageReference Include="Amazon.Lambda.Serialization.SystemTextJson" Version="2.4.4" />
10-
<PackageReference Include="AWSSDK.S3" Version="3.7.410.1" />
10+
<PackageReference Include="AWSSDK.S3" Version="3.7.410.3" />
1111
<PackageReference Include="System.Text.Json" Version="9.0.0" />
1212
</ItemGroup>
1313
<ItemGroup>

Examples/DecryptPgpAndUpload/DecryptPgpAndUpload.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<ItemGroup>
88
<PackageReference Include="Amazon.Lambda.Core" Version="2.5.0" />
99
<PackageReference Include="Amazon.Lambda.Serialization.SystemTextJson" Version="2.4.4" />
10-
<PackageReference Include="AWSSDK.S3" Version="3.7.410.1" />
10+
<PackageReference Include="AWSSDK.S3" Version="3.7.410.3" />
1111
<PackageReference Include="PgpCore" Version="6.5.1" />
1212
<PackageReference Include="SeekableS3Stream" Version="1.0.2" />
1313
<PackageReference Include="System.Text.Json" Version="9.0.0" />

Examples/ZipS3ListToS3/ZipS3ListToS3.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<ItemGroup>
88
<PackageReference Include="Amazon.Lambda.Core" Version="2.5.0" />
99
<PackageReference Include="Amazon.Lambda.Serialization.SystemTextJson" Version="2.4.4" />
10-
<PackageReference Include="AWSSDK.S3" Version="3.7.410.1" />
10+
<PackageReference Include="AWSSDK.S3" Version="3.7.410.3" />
1111
<PackageReference Include="System.Text.Json" Version="9.0.0" />
1212
</ItemGroup>
1313
<ItemGroup>

S3UploadStream/S3UploadStream.cs

Lines changed: 125 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
using System.Collections.Generic;
66
using System.IO;
77
using System.Linq;
8+
using System.Threading;
89
using System.Threading.Tasks;
10+
using Amazon.Runtime;
911

1012
namespace Cppl.Utilities.AWS
1113
{
@@ -21,40 +23,77 @@ public class S3UploadStream : Stream
2123

2224
internal class Metadata
2325
{
24-
public string BucketName;
25-
public string Key;
2626
public long PartLength = DEFAULT_PART_LENGTH;
2727

2828
public int PartCount = 0;
2929
public string UploadId;
3030
public MemoryStream CurrentStream;
31+
public CancellationToken CancellationToken;
3132

3233
public long Position = 0; // based on bytes written
3334
public long Length = 0; // based on bytes written or SetLength, whichever is larger (no truncation)
3435

3536
public List<Task> Tasks = new List<Task>();
36-
public ConcurrentDictionary<int, string> PartETags = new ConcurrentDictionary<int, string>();
37+
public ConcurrentDictionary<int, PartETag> PartETags = new ConcurrentDictionary<int, PartETag>();
38+
39+
public InitiateMultipartUploadRequest InitiateMultipartUploadRequest;
3740
}
3841

39-
Metadata _metadata = new Metadata();
40-
IAmazonS3 _s3 = null;
42+
private readonly IAmazonS3 _s3 = null;
43+
private Metadata _metadata = new Metadata();
44+
45+
public event Action<InitiateMultipartUploadResponse> Initiated;
46+
public event Action<UploadPartResponse> UploadedPart;
47+
public event Action<StreamTransferProgressArgs> StreamTransfer;
48+
public event Action<CompleteMultipartUploadResponse> Completed;
49+
4150

42-
public S3UploadStream(IAmazonS3 s3, string s3uri, long partLength = DEFAULT_PART_LENGTH)
43-
: this(s3, new Uri(s3uri), partLength)
51+
public S3UploadStream(
52+
IAmazonS3 s3,
53+
string s3uri,
54+
long partLength = DEFAULT_PART_LENGTH,
55+
CancellationToken token = default)
56+
: this(s3, new Uri(s3uri), partLength, token)
4457
{
4558
}
4659

47-
public S3UploadStream(IAmazonS3 s3, Uri s3uri, long partLength = DEFAULT_PART_LENGTH)
48-
: this (s3, s3uri.Host, s3uri.LocalPath.Substring(1), partLength)
60+
public S3UploadStream(
61+
IAmazonS3 s3,
62+
Uri s3uri,
63+
long partLength = DEFAULT_PART_LENGTH,
64+
CancellationToken token = default)
65+
: this(s3, s3uri.Host, s3uri.LocalPath.Substring(1), partLength, token)
4966
{
5067
}
5168

52-
public S3UploadStream(IAmazonS3 s3, string bucket, string key, long partLength = DEFAULT_PART_LENGTH)
69+
public S3UploadStream(
70+
IAmazonS3 s3,
71+
string bucket,
72+
string key,
73+
long partLength = DEFAULT_PART_LENGTH,
74+
CancellationToken token = default)
75+
: this(
76+
s3,
77+
new InitiateMultipartUploadRequest
78+
{
79+
BucketName = bucket,
80+
Key = key
81+
},
82+
partLength,
83+
token)
84+
{
85+
}
86+
87+
public S3UploadStream(
88+
IAmazonS3 s3,
89+
InitiateMultipartUploadRequest initiateMultipartUploadRequest,
90+
long partLength = DEFAULT_PART_LENGTH,
91+
CancellationToken token = default)
5392
{
5493
_s3 = s3;
55-
_metadata.BucketName = bucket;
56-
_metadata.Key = key;
5794
_metadata.PartLength = partLength;
95+
_metadata.InitiateMultipartUploadRequest = initiateMultipartUploadRequest;
96+
_metadata.CancellationToken = token;
5897
}
5998

6099
protected override void Dispose(bool disposing)
@@ -67,10 +106,11 @@ protected override void Dispose(bool disposing)
67106
CompleteUpload();
68107
}
69108
}
109+
70110
_metadata = null;
71111
base.Dispose(disposing);
72112
}
73-
113+
74114
public override bool CanRead => false;
75115
public override bool CanSeek => false;
76116
public override bool CanWrite => true;
@@ -83,21 +123,26 @@ public override long Position
83123
}
84124

85125
public override int Read(byte[] buffer, int offset, int count) => throw new NotImplementedException();
126+
86127
public override long Seek(long offset, SeekOrigin origin) => throw new NotImplementedException();
87128

88129
public override void SetLength(long value)
89130
{
90131
_metadata.Length = Math.Max(_metadata.Length, value);
91-
_metadata.PartLength = Math.Max(MIN_PART_LENGTH, Math.Min(MAX_PART_LENGTH, _metadata.Length / MAX_PART_COUNT));
132+
_metadata.PartLength =
133+
Math.Max(MIN_PART_LENGTH, Math.Min(MAX_PART_LENGTH, _metadata.Length / MAX_PART_COUNT));
92134
}
93135

94136
private void StartNewPart()
95137
{
96-
if (_metadata.CurrentStream != null) {
138+
if (_metadata.CurrentStream != null)
139+
{
97140
Flush(false);
98141
}
142+
99143
_metadata.CurrentStream = new MemoryStream();
100-
_metadata.PartLength = Math.Min(MAX_PART_LENGTH, Math.Max(_metadata.PartLength, (_metadata.PartCount / 2 + 1) * MIN_PART_LENGTH));
144+
_metadata.PartLength = Math.Min(MAX_PART_LENGTH,
145+
Math.Max(_metadata.PartLength, (_metadata.PartCount / 2 + 1) * MIN_PART_LENGTH));
101146
}
102147

103148
public override void Flush()
@@ -109,39 +154,60 @@ private void Flush(bool disposing)
109154
{
110155
if ((_metadata.CurrentStream == null || _metadata.CurrentStream.Length < MIN_PART_LENGTH) &&
111156
!disposing)
157+
{
112158
return;
159+
}
113160

114-
if (_metadata.UploadId == null) {
115-
_metadata.UploadId = _s3.InitiateMultipartUploadAsync(new InitiateMultipartUploadRequest()
116-
{
117-
BucketName = _metadata.BucketName,
118-
Key = _metadata.Key
119-
}).GetAwaiter().GetResult().UploadId;
161+
if (_metadata.UploadId == null)
162+
{
163+
var response = _s3
164+
.InitiateMultipartUploadAsync(_metadata.InitiateMultipartUploadRequest, _metadata.CancellationToken)
165+
.GetAwaiter().GetResult();
166+
_metadata.CancellationToken.ThrowIfCancellationRequested();
167+
Initiated?.Invoke(response);
168+
_metadata.UploadId = response.UploadId;
120169
}
121-
170+
122171
if (_metadata.CurrentStream != null)
123172
{
124173
var i = ++_metadata.PartCount;
125174

126175
_metadata.CurrentStream.Seek(0, SeekOrigin.Begin);
127176
var request = new UploadPartRequest()
128177
{
129-
BucketName = _metadata.BucketName,
130-
Key = _metadata.Key,
178+
BucketName = _metadata.InitiateMultipartUploadRequest.BucketName,
179+
Key = _metadata.InitiateMultipartUploadRequest.Key,
131180
UploadId = _metadata.UploadId,
132181
PartNumber = i,
133182
IsLastPart = disposing,
134-
InputStream = _metadata.CurrentStream
183+
InputStream = _metadata.CurrentStream,
184+
ChecksumAlgorithm = _metadata.InitiateMultipartUploadRequest.ChecksumAlgorithm,
135185
};
136186
_metadata.CurrentStream = null;
187+
request.StreamTransferProgress += (_, progressArgs) => { StreamTransfer?.Invoke(progressArgs); };
137188

138-
var upload = Task.Run(async () =>
139-
{
140-
var response = await _s3.UploadPartAsync(request);
141-
_metadata.PartETags.AddOrUpdate(i, response.ETag,
142-
(n, s) => response.ETag);
143-
request.InputStream.Dispose();
144-
});
189+
var upload = Task.Run(
190+
async () =>
191+
{
192+
var response = await _s3.UploadPartAsync(request, _metadata.CancellationToken);
193+
194+
_metadata.CancellationToken.ThrowIfCancellationRequested();
195+
196+
UploadedPart?.Invoke(response);
197+
198+
var partETag = new PartETag
199+
{
200+
PartNumber = response.PartNumber,
201+
ETag = response.ETag,
202+
ChecksumSHA1 = response.ChecksumSHA1,
203+
ChecksumSHA256 = response.ChecksumSHA256,
204+
ChecksumCRC32 = response.ChecksumCRC32,
205+
ChecksumCRC32C = response.ChecksumCRC32C,
206+
};
207+
208+
_metadata.PartETags.AddOrUpdate(i, partETag, (n, s) => partETag);
209+
request.InputStream.Dispose();
210+
});
145211
_metadata.Tasks.Add(upload);
146212
}
147213
}
@@ -150,14 +216,19 @@ private void CompleteUpload()
150216
{
151217
Task.WaitAll(_metadata.Tasks.ToArray());
152218

153-
if (Length > 0) {
154-
_s3.CompleteMultipartUploadAsync(new CompleteMultipartUploadRequest()
155-
{
156-
BucketName = _metadata.BucketName,
157-
Key = _metadata.Key,
158-
PartETags = _metadata.PartETags.Select(e => new PartETag(e.Key, e.Value)).ToList(),
159-
UploadId = _metadata.UploadId
160-
}).GetAwaiter().GetResult();
219+
if (Length > 0)
220+
{
221+
_metadata.CancellationToken.ThrowIfCancellationRequested();
222+
223+
var response = _s3.CompleteMultipartUploadAsync(
224+
new CompleteMultipartUploadRequest()
225+
{
226+
BucketName = _metadata.InitiateMultipartUploadRequest.BucketName,
227+
Key = _metadata.InitiateMultipartUploadRequest.Key,
228+
PartETags = _metadata.PartETags.Values.ToList(),
229+
UploadId = _metadata.UploadId,
230+
}, _metadata.CancellationToken).GetAwaiter().GetResult();
231+
Completed?.Invoke(response);
161232
}
162233
}
163234

@@ -171,8 +242,20 @@ public override void Write(byte[] buffer, int offset, int count)
171242
var c = Math.Min(count, buffer.Length - offset); // don't over-read the buffer, even if asked to
172243
do
173244
{
245+
if (_metadata.CancellationToken.IsCancellationRequested)
246+
{
247+
return;
248+
}
249+
174250
if (_metadata.CurrentStream == null || _metadata.CurrentStream.Length >= _metadata.PartLength)
251+
{
175252
StartNewPart();
253+
}
254+
255+
if (_metadata.CurrentStream == null)
256+
{
257+
throw new ArgumentNullException(nameof(Metadata.CurrentStream));
258+
}
176259

177260
var remaining = _metadata.PartLength - _metadata.CurrentStream.Length;
178261
var w = Math.Min(c, (int)remaining);
@@ -184,4 +267,4 @@ public override void Write(byte[] buffer, int offset, int count)
184267
} while (c > 0);
185268
}
186269
}
187-
}
270+
}

S3UploadStream/S3UploadStream.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
</PropertyGroup>
1616

1717
<ItemGroup>
18-
<PackageReference Include="AWSSDK.S3" Version="3.7.410.1" />
18+
<PackageReference Include="AWSSDK.S3" Version="3.7.410.3" />
1919
<None Include="../Readme.md" Pack="true" PackagePath="\" />
2020
</ItemGroup>
2121

0 commit comments

Comments
 (0)