25
25
#include < aws/s3/model/ListObjectsRequest.h>
26
26
#include < aws/s3/model/DeleteObjectRequest.h>
27
27
#include < aws/core/auth/AWSCredentialsProvider.h>
28
- #include < aws/core/utils/memory/stl/AWSStringStream.h>
29
28
#include < aws/core/utils/HashingUtils.h>
29
+ #include < aws/core/utils/memory/stl/AWSStreamFwd.h>
30
+ #include < aws/core/utils/memory/stl/AWSStringStream.h>
31
+ #include < aws/core/utils/memory/AWSMemory.h>
32
+ #include < aws/core/utils/stream/PreallocatedStreamBuf.h>
33
+ #include < aws/core/utils/StringUtils.h>
34
+ #include < aws/transfer/TransferManager.h>
30
35
#include < aws/crt/Api.h>
36
+ #include < fstream>
31
37
32
38
#include < boost/lexical_cast.hpp>
39
+ #include < boost/interprocess/streams/bufferstream.hpp>
33
40
#include < iostream>
34
41
#include < fstream>
35
42
@@ -39,13 +46,16 @@ class AwsS3StoragePlugin : public BaseStorage
39
46
{
40
47
public:
41
48
42
- Aws::S3::S3Client client_;
43
49
std::string bucketName_;
44
50
bool storageContainsUnknownFiles_;
51
+ bool useTransferManager_;
52
+ std::shared_ptr<Aws::S3::S3Client> client_;
53
+ std::shared_ptr<Aws::Utils::Threading::Executor> executor_;
54
+ std::shared_ptr<Aws::Transfer::TransferManager> transferManager_;
45
55
46
56
public:
47
57
48
- AwsS3StoragePlugin (const std::string& nameForLogs, const Aws::S3::S3Client& client, const std::string& bucketName, bool enableLegacyStorageStructure, bool storageContainsUnknownFiles);
58
+ AwsS3StoragePlugin (const std::string& nameForLogs, std::shared_ptr< Aws::S3::S3Client> client, const std::string& bucketName, bool enableLegacyStorageStructure, bool storageContainsUnknownFiles, bool useTransferManager );
49
59
50
60
virtual ~AwsS3StoragePlugin ();
51
61
@@ -55,21 +65,21 @@ class AwsS3StoragePlugin : public BaseStorage
55
65
};
56
66
57
67
58
- class Writer : public IStorage ::IWriter
68
+ class DirectWriter : public IStorage ::IWriter
59
69
{
60
70
std::string path_;
61
- Aws::S3::S3Client client_;
71
+ std::shared_ptr< Aws::S3::S3Client> client_;
62
72
std::string bucketName_;
63
73
64
74
public:
65
- Writer ( const Aws::S3::S3Client& client, const std::string& bucketName, const std::string& path)
75
+ DirectWriter (std::shared_ptr< Aws::S3::S3Client> client, const std::string& bucketName, const std::string& path)
66
76
: path_(path),
67
77
client_ (client),
68
78
bucketName_(bucketName)
69
79
{
70
80
}
71
81
72
- virtual ~Writer ()
82
+ virtual ~DirectWriter ()
73
83
{
74
84
}
75
85
@@ -89,7 +99,7 @@ class Writer : public IStorage::IWriter
89
99
putObjectRequest.SetBody (stream);
90
100
putObjectRequest.SetContentMD5 (Aws::Utils::HashingUtils::Base64Encode (Aws::Utils::HashingUtils::CalculateMD5 (*stream)));
91
101
92
- auto result = client_. PutObject (putObjectRequest);
102
+ auto result = client_-> PutObject (putObjectRequest);
93
103
94
104
if (!result.IsSuccess ())
95
105
{
@@ -99,23 +109,24 @@ class Writer : public IStorage::IWriter
99
109
};
100
110
101
111
102
- class Reader : public IStorage ::IReader
112
+ class DirectReader : public IStorage ::IReader
103
113
{
104
- Aws::S3::S3Client client_;
114
+ protected:
115
+ std::shared_ptr<Aws::S3::S3Client> client_;
105
116
std::string bucketName_;
106
117
std::list<std::string> paths_;
107
118
std::string uuid_;
108
119
109
120
public:
110
- Reader ( const Aws::S3::S3Client& client, const std::string& bucketName, const std::list<std::string>& paths, const char * uuid)
121
+ DirectReader (std::shared_ptr< Aws::S3::S3Client> client, const std::string& bucketName, const std::list<std::string>& paths, const char * uuid)
111
122
: client_(client),
112
123
bucketName_ (bucketName),
113
124
paths_(paths),
114
125
uuid_(uuid)
115
126
{
116
127
}
117
128
118
- virtual ~Reader ()
129
+ virtual ~DirectReader ()
119
130
{
120
131
121
132
}
@@ -160,7 +171,7 @@ class Reader : public IStorage::IReader
160
171
listObjectRequest.SetBucket (bucketName_.c_str ());
161
172
listObjectRequest.SetPrefix (path.c_str ());
162
173
163
- auto result = client_. ListObjects (listObjectRequest);
174
+ auto result = client_-> ListObjects (listObjectRequest);
164
175
165
176
if (result.IsSuccess ())
166
177
{
@@ -232,7 +243,7 @@ class Reader : public IStorage::IReader
232
243
});
233
244
234
245
// Get the object
235
- auto result = client_. GetObject (getObjectRequest);
246
+ auto result = client_-> GetObject (getObjectRequest);
236
247
if (result.IsSuccess ())
237
248
{
238
249
}
@@ -246,6 +257,111 @@ class Reader : public IStorage::IReader
246
257
247
258
248
259
260
+ class TransferWriter : public IStorage ::IWriter
261
+ {
262
+ std::string path_;
263
+ std::shared_ptr<Aws::Transfer::TransferManager> transferManager_;
264
+ std::string bucketName_;
265
+
266
+ public:
267
+ TransferWriter (std::shared_ptr<Aws::Transfer::TransferManager> transferManager, const std::string& bucketName, const std::string& path)
268
+ : path_(path),
269
+ transferManager_ (transferManager),
270
+ bucketName_(bucketName)
271
+ {
272
+ }
273
+
274
+ virtual ~TransferWriter ()
275
+ {
276
+ }
277
+
278
+ virtual void Write (const char * data, size_t size)
279
+ {
280
+ boost::interprocess::bufferstream buffer (const_cast <char *>(static_cast <const char *>(data)), static_cast <size_t >(size));
281
+ std::shared_ptr<Aws::IOStream> body = Aws::MakeShared<Aws::IOStream>(ALLOCATION_TAG, buffer.rdbuf ());
282
+
283
+ std::shared_ptr<Aws::Transfer::TransferHandle> transferHandle = transferManager_->UploadFile (body, bucketName_, path_.c_str (), " application/binary" , Aws::Map<Aws::String, Aws::String>());
284
+ transferHandle->WaitUntilFinished ();
285
+
286
+ if (transferHandle->GetStatus () != Aws::Transfer::TransferStatus::COMPLETED)
287
+ {
288
+ throw StoragePluginException (std::string (" error while writing file " ) + path_ + " : response code = " + boost::lexical_cast<std::string>(static_cast <int >(transferHandle->GetLastError ().GetResponseCode ())) + " " + transferHandle->GetLastError ().GetMessage ());
289
+ }
290
+ }
291
+ };
292
+
293
+
294
+ class TransferReader : public DirectReader
295
+ {
296
+ std::shared_ptr<Aws::Transfer::TransferManager> transferManager_;
297
+
298
+ public:
299
+ TransferReader (std::shared_ptr<Aws::Transfer::TransferManager> transferManager, std::shared_ptr<Aws::S3::S3Client> client, const std::string& bucketName, const std::list<std::string>& paths, const char * uuid)
300
+ : DirectReader(client, bucketName, paths, uuid),
301
+ transferManager_ (transferManager)
302
+ {
303
+ }
304
+
305
+ virtual ~TransferReader ()
306
+ {
307
+
308
+ }
309
+
310
+ virtual void ReadWhole (char * data, size_t size)
311
+ {
312
+ std::string firstExceptionMessage;
313
+
314
+ for (auto & path: paths_)
315
+ {
316
+ try
317
+ {
318
+ // The local variable 'streamBuffer' is captured by reference in a lambda.
319
+ // It must persist until all downloading by the 'transfer_manager' is complete.
320
+ Aws::Utils::Stream::PreallocatedStreamBuf streamBuffer (reinterpret_cast <unsigned char *>(data), size);
321
+
322
+ std::shared_ptr<Aws::Transfer::TransferHandle> downloadHandler = transferManager_->DownloadFile (bucketName_, path, [&]() { // Define a lambda expression for the callback method parameter to stream back the data.
323
+ return Aws::New<Aws::IOStream>(ALLOCATION_TAG, &streamBuffer);
324
+ });
325
+
326
+ downloadHandler->WaitUntilFinished ();
327
+
328
+ if (downloadHandler->GetStatus () == Aws::Transfer::TransferStatus::COMPLETED)
329
+ {
330
+ return ;
331
+ }
332
+ else if (firstExceptionMessage.empty ())
333
+ {
334
+ firstExceptionMessage = downloadHandler->GetLastError ().GetMessage ();
335
+ }
336
+ // getObjectRequest.SetResponseStreamFactory(
337
+ // [data, size]()
338
+ // {
339
+ // std::unique_ptr<Aws::StringStream>
340
+ // istream(Aws::New<Aws::StringStream>(ALLOCATION_TAG));
341
+
342
+ // istream->rdbuf()->pubsetbuf(static_cast<char*>(data),
343
+ // size);
344
+
345
+ // return istream.release();
346
+ // });
347
+
348
+
349
+ }
350
+ catch (StoragePluginException& ex)
351
+ {
352
+ if (firstExceptionMessage.empty ())
353
+ {
354
+ firstExceptionMessage = ex.what ();
355
+ }
356
+ // ignore to retry
357
+ }
358
+ }
359
+ throw StoragePluginException (firstExceptionMessage);
360
+ }
361
+
362
+ };
363
+
364
+
249
365
250
366
251
367
const char * AwsS3StoragePluginFactory::GetStoragePluginName ()
@@ -340,26 +456,29 @@ IStorage* AwsS3StoragePluginFactory::CreateStorage(const std::string& nameForLog
340
456
configuration.caFile = caFile;
341
457
}
342
458
459
+ bool useTransferManager = true ; // new in v 2.3.0
460
+ pluginSection.LookupBooleanValue (useTransferManager, " UseTransferManager" );
461
+
343
462
if (pluginSection.LookupStringValue (accessKey, " AccessKey" ) && pluginSection.LookupStringValue (secretKey, " SecretKey" ))
344
463
{
345
464
OrthancPlugins::LogInfo (" AWS S3 Storage: using credentials from the configuration file" );
346
465
Aws::Auth::AWSCredentials credentials (accessKey.c_str (), secretKey.c_str ());
347
466
348
- Aws::S3::S3Client client ( credentials, configuration, Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never, virtualAddressing);
467
+ std::shared_ptr< Aws::S3::S3Client> client = Aws::MakeShared<Aws::S3::S3Client>(ALLOCATION_TAG, credentials, configuration, Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never, virtualAddressing);
349
468
350
469
OrthancPlugins::LogInfo (" AWS S3 storage initialized" );
351
470
352
- return new AwsS3StoragePlugin (nameForLogs, client, bucketName, enableLegacyStorageStructure, storageContainsUnknownFiles);
471
+ return new AwsS3StoragePlugin (nameForLogs, client, bucketName, enableLegacyStorageStructure, storageContainsUnknownFiles, useTransferManager );
353
472
}
354
473
else
355
474
{
356
475
// when using default credentials, credentials are not checked at startup but only the first time you try to access the bucket !
357
476
OrthancPlugins::LogInfo (" AWS S3 Storage: using default credentials provider" );
358
- Aws::S3::S3Client client ( configuration, Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never, virtualAddressing);
477
+ std::shared_ptr< Aws::S3::S3Client> client = Aws::MakeShared<Aws::S3::S3Client>(ALLOCATION_TAG, configuration, Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never, virtualAddressing);
359
478
360
479
OrthancPlugins::LogInfo (" AWS S3 storage initialized" );
361
480
362
- return new AwsS3StoragePlugin (nameForLogs, client, bucketName, enableLegacyStorageStructure, storageContainsUnknownFiles);
481
+ return new AwsS3StoragePlugin (nameForLogs, client, bucketName, enableLegacyStorageStructure, storageContainsUnknownFiles, useTransferManager );
363
482
}
364
483
}
365
484
catch (const std::exception & e)
@@ -379,17 +498,33 @@ AwsS3StoragePlugin::~AwsS3StoragePlugin()
379
498
}
380
499
381
500
382
- AwsS3StoragePlugin::AwsS3StoragePlugin (const std::string& nameForLogs, const Aws::S3::S3Client& client, const std::string& bucketName, bool enableLegacyStorageStructure, bool storageContainsUnknownFiles)
501
+ AwsS3StoragePlugin::AwsS3StoragePlugin (const std::string& nameForLogs, std::shared_ptr< Aws::S3::S3Client> client, const std::string& bucketName, bool enableLegacyStorageStructure, bool storageContainsUnknownFiles, bool useTransferManager )
383
502
: BaseStorage(nameForLogs, enableLegacyStorageStructure),
384
- client_(client),
385
503
bucketName_(bucketName),
386
- storageContainsUnknownFiles_(storageContainsUnknownFiles)
504
+ storageContainsUnknownFiles_(storageContainsUnknownFiles),
505
+ useTransferManager_(useTransferManager),
506
+ client_(client)
387
507
{
508
+ if (useTransferManager_)
509
+ {
510
+ executor_ = Aws::MakeShared<Aws::Utils::Threading::PooledThreadExecutor>(ALLOCATION_TAG, 10 );
511
+ Aws::Transfer::TransferManagerConfiguration transferConfig (executor_.get ());
512
+ transferConfig.s3Client = client_;
513
+
514
+ transferManager_ = Aws::Transfer::TransferManager::Create (transferConfig);
515
+ }
388
516
}
389
517
390
518
IStorage::IWriter* AwsS3StoragePlugin::GetWriterForObject (const char * uuid, OrthancPluginContentType type, bool encryptionEnabled)
391
519
{
392
- return new Writer (client_, bucketName_, GetPath (uuid, type, encryptionEnabled));
520
+ if (useTransferManager_)
521
+ {
522
+ return new TransferWriter (transferManager_, bucketName_, GetPath (uuid, type, encryptionEnabled));
523
+ }
524
+ else
525
+ {
526
+ return new DirectWriter (client_, bucketName_, GetPath (uuid, type, encryptionEnabled));
527
+ }
393
528
}
394
529
395
530
IStorage::IReader* AwsS3StoragePlugin::GetReaderForObject (const char * uuid, OrthancPluginContentType type, bool encryptionEnabled)
@@ -401,7 +536,14 @@ IStorage::IReader* AwsS3StoragePlugin::GetReaderForObject(const char* uuid, Orth
401
536
paths.push_back (GetPath (uuid, type, encryptionEnabled, true ));
402
537
}
403
538
404
- return new Reader (client_, bucketName_, paths, uuid);
539
+ if (useTransferManager_)
540
+ {
541
+ return new TransferReader (transferManager_, client_, bucketName_, paths, uuid);
542
+ }
543
+ else
544
+ {
545
+ return new DirectReader (client_, bucketName_, paths, uuid);
546
+ }
405
547
}
406
548
407
549
void AwsS3StoragePlugin::DeleteObject (const char * uuid, OrthancPluginContentType type, bool encryptionEnabled)
@@ -422,7 +564,7 @@ void AwsS3StoragePlugin::DeleteObject(const char* uuid, OrthancPluginContentType
422
564
deleteObjectRequest.SetBucket (bucketName_.c_str ());
423
565
deleteObjectRequest.SetKey (path.c_str ());
424
566
425
- auto result = client_. DeleteObject (deleteObjectRequest);
567
+ auto result = client_-> DeleteObject (deleteObjectRequest);
426
568
427
569
if (!result.IsSuccess () && firstExceptionMessage.empty ())
428
570
{
0 commit comments