Mercurial > hg > orthanc-object-storage
diff Aws/AwsS3StoragePlugin.cpp @ 1:fc26a8fc54d5
initial release
author | Alain Mazy <alain@mazy.be> |
---|---|
date | Fri, 03 Jul 2020 10:08:44 +0200 |
parents | |
children | 393fcf337462 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Aws/AwsS3StoragePlugin.cpp Fri Jul 03 10:08:44 2020 +0200 @@ -0,0 +1,298 @@ +/** + * Cloud storage plugins for Orthanc + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#include "AwsS3StoragePlugin.h" + +#include <aws/core/Aws.h> +#include <aws/s3/S3Client.h> +#include <aws/s3/model/PutObjectRequest.h> +#include <aws/s3/model/GetObjectRequest.h> +#include <aws/s3/model/ListObjectsRequest.h> +#include <aws/s3/model/DeleteObjectRequest.h> +#include <aws/core/auth/AWSCredentialsProvider.h> +#include <aws/core/utils/memory/stl/AWSStringStream.h> + +#include <boost/lexical_cast.hpp> +#include <iostream> +#include <fstream> + +const char* ALLOCATION_TAG = "OrthancS3"; + +class AwsS3StoragePlugin : public IStoragePlugin +{ +public: + + Aws::S3::S3Client client_; + std::string bucketName_; + +public: + + AwsS3StoragePlugin(const Aws::S3::S3Client& client, const std::string& bucketName); + + virtual IWriter* GetWriterForObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled); + virtual IReader* GetReaderForObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled); + virtual void DeleteObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled); +private: + virtual std::string GetPath(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled); +}; + + +class Writer : public IStoragePlugin::IWriter +{ + std::string path_; + Aws::S3::S3Client client_; + std::string bucketName_; + +public: + Writer(const Aws::S3::S3Client& client, const std::string& bucketName, const std::string& path) + : path_(path), + client_(client), + bucketName_(bucketName) + { + } + + virtual ~Writer() + { + } + + virtual void Write(const char* data, size_t size) + { + Aws::S3::Model::PutObjectRequest putObjectRequest; + + putObjectRequest.SetBucket(bucketName_.c_str()); + putObjectRequest.SetKey(path_.c_str()); + + std::shared_ptr<Aws::StringStream> stream = Aws::MakeShared<Aws::StringStream>(ALLOCATION_TAG, std::ios_base::in | std::ios_base::binary); + + stream->rdbuf()->pubsetbuf(const_cast<char*>(data), size); + stream->rdbuf()->pubseekpos(size); + stream->seekg(0); + + putObjectRequest.SetBody(stream); + + auto result = client_.PutObject(putObjectRequest); + + if (!result.IsSuccess()) + { + throw StoragePluginException(std::string("error while writing file ") + path_ + ": " + result.GetError().GetExceptionName().c_str() + " " + result.GetError().GetMessage().c_str()); + } + } +}; + + +class Reader : public IStoragePlugin::IReader +{ + std::string path_; + Aws::S3::S3Client client_; + std::string bucketName_; + +public: + Reader(const Aws::S3::S3Client& client, const std::string& bucketName, const std::string& path) + : path_(path), + client_(client), + bucketName_(bucketName) + { + } + + virtual ~Reader() + { + + } + virtual size_t GetSize() + { + Aws::S3::Model::ListObjectsRequest listObjectRequest; + listObjectRequest.SetBucket(bucketName_.c_str()); + listObjectRequest.SetPrefix(path_.c_str()); + + auto result = client_.ListObjects(listObjectRequest); + + if (result.IsSuccess()) + { + Aws::Vector<Aws::S3::Model::Object> objectList = + result.GetResult().GetContents(); + + if (objectList.size() == 1) + { + return objectList[0].GetSize(); + } + throw StoragePluginException(std::string("error while reading file ") + path_ + ": multiple objet with same name !"); + } + else + { + throw StoragePluginException(std::string("error while reading file ") + path_ + ": " + result.GetError().GetExceptionName().c_str() + " " + result.GetError().GetMessage().c_str()); + } + } + + virtual void Read(char* data, size_t size) + { + Aws::S3::Model::GetObjectRequest getObjectRequest; + getObjectRequest.SetBucket(bucketName_.c_str()); + getObjectRequest.SetKey(path_.c_str()); + + getObjectRequest.SetResponseStreamFactory( + [data, size]() + { + std::unique_ptr<Aws::StringStream> + istream(Aws::New<Aws::StringStream>(ALLOCATION_TAG)); + + istream->rdbuf()->pubsetbuf(static_cast<char*>(data), + size); + + return istream.release(); + }); + + // Get the object + auto result = client_.GetObject(getObjectRequest); + if (result.IsSuccess()) + { + } + else + { + throw StoragePluginException(std::string("error while reading file ") + path_ + ": " + result.GetError().GetExceptionName().c_str() + " " + result.GetError().GetMessage().c_str()); + } + } + +}; + + + +const char* AwsS3StoragePluginFactory::GetStoragePluginName() +{ + return "AWS S3 Storage"; +} + +IStoragePlugin* AwsS3StoragePluginFactory::CreateStoragePlugin(const OrthancPlugins::OrthancConfiguration& orthancConfig) +{ + static const char* const PLUGIN_SECTION = "AwsS3Storage"; + if (!orthancConfig.IsSection(PLUGIN_SECTION)) + { + OrthancPlugins::LogWarning(std::string(GetStoragePluginName()) + " plugin, section missing. Plugin is not enabled."); + return nullptr; + } + + OrthancPlugins::OrthancConfiguration pluginSection; + orthancConfig.GetSection(pluginSection, PLUGIN_SECTION); + + std::string bucketName; + std::string region; + std::string accessKey; + std::string secretKey; + + if (!pluginSection.LookupStringValue(bucketName, "BucketName")) + { + OrthancPlugins::LogError("AwsS3Storage/BucketName configuration missing. Unable to initialize plugin"); + return nullptr; + } + + if (!pluginSection.LookupStringValue(region, "Region")) + { + OrthancPlugins::LogError("AwsS3Storage/Region configuration missing. Unable to initialize plugin"); + return nullptr; + } + + if (!pluginSection.LookupStringValue(accessKey, "AccessKey")) + { + OrthancPlugins::LogError("AwsS3Storage/AccessKey configuration missing. Unable to initialize plugin"); + return nullptr; + } + + if (!pluginSection.LookupStringValue(secretKey, "SecretKey")) + { + OrthancPlugins::LogError("AwsS3Storage/SecretKey configuration missing. Unable to initialize plugin"); + return nullptr; + } + + try + { + Aws::SDKOptions options; + Aws::InitAPI(options); + + Aws::Auth::AWSCredentials credentials(accessKey.c_str(), secretKey.c_str()); + Aws::Client::ClientConfiguration configuration; + configuration.region = region.c_str(); + Aws::S3::S3Client client(credentials, configuration); + + OrthancPlugins::LogInfo("AWS S3 storage initialized"); + + return new AwsS3StoragePlugin(client, bucketName); + } + catch (const std::exception& e) + { + OrthancPlugins::LogError(std::string("AzureBlobStorage plugin: failed to initialize plugin: ") + e.what()); + return nullptr; + } + +} + +AwsS3StoragePlugin::AwsS3StoragePlugin(const Aws::S3::S3Client& client, const std::string& bucketName) + : client_(client), + bucketName_(bucketName) +{ + +} + +IStoragePlugin::IWriter* AwsS3StoragePlugin::GetWriterForObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled) +{ + return new Writer(client_, bucketName_, GetPath(uuid, type, encryptionEnabled)); +} + +IStoragePlugin::IReader* AwsS3StoragePlugin::GetReaderForObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled) +{ + return new Reader(client_, bucketName_, GetPath(uuid, type, encryptionEnabled)); +} + +void AwsS3StoragePlugin::DeleteObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled) +{ + std::string path = GetPath(uuid, type, encryptionEnabled); + + Aws::S3::Model::DeleteObjectRequest deleteObjectRequest; + deleteObjectRequest.SetBucket(bucketName_.c_str()); + deleteObjectRequest.SetKey(path.c_str()); + + auto result = client_.DeleteObject(deleteObjectRequest); + + if (!result.IsSuccess()) + { + throw StoragePluginException(std::string("error while deleting file ") + path + ": " + result.GetError().GetExceptionName().c_str() + " " + result.GetError().GetMessage().c_str()); + } + +} + +std::string AwsS3StoragePlugin::GetPath(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled) +{ + std::string path = std::string(uuid); + + if (type == OrthancPluginContentType_Dicom) + { + path += ".dcm"; + } + else if (type == OrthancPluginContentType_DicomAsJson) + { + path += ".json"; + } + else + { + path += ".unk"; + } + + if (encryptionEnabled) + { + path += ".enc"; + } + return path; +}