/**
 * Cloud storage plugins for Orthanc
 * Copyright (C) 2020-2021 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 <Logging.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/HashingUtils.h>
#include <aws/core/utils/logging/DefaultLogSystem.h>
#include <aws/core/utils/logging/DefaultCRTLogSystem.h>
#include <aws/core/utils/logging/AWSLogging.h>
#include <aws/core/utils/memory/stl/AWSStreamFwd.h>
#include <aws/core/utils/memory/stl/AWSStringStream.h>
#include <aws/core/utils/memory/AWSMemory.h>
#include <aws/core/utils/stream/PreallocatedStreamBuf.h>
#include <aws/core/utils/StringUtils.h>
#include <aws/transfer/TransferManager.h>
#include <aws/crt/Api.h>
#include <iostream>
#include <fstream>

#include <boost/lexical_cast.hpp>
#include <boost/interprocess/streams/bufferstream.hpp>
#include <iostream>
#include <fstream>

const char* ALLOCATION_TAG = "OrthancS3";

class AwsS3StoragePlugin : public BaseStorage
{
public:

    std::string             bucketName_;
    bool                    storageContainsUnknownFiles_;
    bool                    useTransferManager_;
    std::shared_ptr<Aws::S3::S3Client>               client_;
    std::shared_ptr<Aws::Utils::Threading::Executor> executor_;
    std::shared_ptr<Aws::Transfer::TransferManager>  transferManager_;

public:

    AwsS3StoragePlugin(const std::string& nameForLogs, std::shared_ptr<Aws::S3::S3Client> client, const std::string& bucketName, bool enableLegacyStorageStructure, bool storageContainsUnknownFiles, bool useTransferManager, unsigned int transferThreadPoolSize, unsigned int transferBufferSizeMB);

    virtual ~AwsS3StoragePlugin();

    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);
    virtual bool HasFileExists() { return false; };
};


class DirectWriter : public IStorage::IWriter
{
    std::string             path_;
    std::shared_ptr<Aws::S3::S3Client>       client_;
    std::string             bucketName_;

public:
    DirectWriter(std::shared_ptr<Aws::S3::S3Client> client, const std::string& bucketName, const std::string& path)
        : path_(path),
        client_(client),
        bucketName_(bucketName)
    {
    }

    virtual ~DirectWriter()
    {
    }

    virtual void Write(const char* data, size_t size)
    {
        OrthancPlugins::LogWarning(std::string("--AWS S3 Storage plugin: Write: ") + std::to_string(size));
        OrthancPlugins::LogWarning(std::string("--AWS S3 Storage plugin: data: ") + data);

        //std::ofstream pp("d:/55.dat",std::ios_base::out| std::ios_base::binary);
        //pp.write(data, size);
       // pp.close();

        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);
        putObjectRequest.SetContentMD5(Aws::Utils::HashingUtils::Base64Encode(Aws::Utils::HashingUtils::CalculateMD5(*stream)));
        */
        // 第二种
        /*
        std::shared_ptr<Aws::StringStream> input_data =
            Aws::MakeShared<Aws::StringStream>("");
        input_data->write(data, size);
        putObjectRequest.SetBody(input_data);
        */

        //第三种
        boost::interprocess::bufferstream buf(const_cast<char*>(static_cast<const char*>(data)), static_cast<size_t>(size));
        auto body = Aws::MakeShared<Aws::IOStream>(ALLOCATION_TAG, buf.rdbuf());

        //std::stringbuf buf(const_cast<char*>(static_cast<const char*>(content)), size);
        //std::shared_ptr<Aws::IOStream> body = Aws::MakeShared<Aws::IOStream>(ALLOCATION_TAG, &buf);

        auto input_data = Aws::MakeShared<Aws::FStream>("PutObjectInputStream",
            path_.c_str(), std::ios_base::in | std::ios_base::binary);

        putObjectRequest.SetBody(body);
        
        auto result = client_->PutObject(putObjectRequest);



       // auto input_data = Aws::MakeShared<Aws::FStream>("PutObjectInputStream", "d:/mr.dcm", std::ios_base::in | std::ios_base::binary);
        if (!result.IsSuccess())
        {
            OrthancPlugins::LogWarning(std::string("--AWS S3 put object fail: ") );
            throw StoragePluginException(std::string("error while writing file ") + path_ + ": response code = " + boost::lexical_cast<std::string>((int)result.GetError().GetResponseCode()) + " " + result.GetError().GetExceptionName().c_str() + " " + result.GetError().GetMessage().c_str());
        }
        OrthancPlugins::LogWarning(std::string("--AWS S3 put object Success: "));
    }
};


class DirectReader : public IStorage::IReader
{
protected:
    std::shared_ptr<Aws::S3::S3Client>       client_;
    std::string             bucketName_;
    std::list<std::string>  paths_;
    std::string             uuid_;

public:
    DirectReader(std::shared_ptr<Aws::S3::S3Client> client, const std::string& bucketName, const std::list<std::string>& paths, const char* uuid)
        : client_(client),
        bucketName_(bucketName),
        paths_(paths),
        uuid_(uuid)
    {
        OrthancPlugins::LogWarning(std::string("--DirectReader c---: "));
    }

    virtual ~DirectReader()
    {

    }

    virtual size_t GetSize()
    {
        std::string firstExceptionMessage;

        for (auto& path : paths_)
        {
            try
            {
                return _GetSize(path);
            }
            catch (StoragePluginException& ex)
            {
                if (firstExceptionMessage.empty())
                {
                    firstExceptionMessage = ex.what();
                }
                //ignore to retry
            }
        }
        throw StoragePluginException(firstExceptionMessage);
    }

    virtual void ReadWhole(char* data, size_t size)
    {
        _Read(data, size, 0, false);
    }

    virtual void ReadRange(char* data, size_t size, size_t fromOffset)
    {
        _Read(data, size, fromOffset, true);
    }

private:

    size_t _GetSize(const std::string& path)
    {
        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();
            }
            else if (objectList.size() > 1)
            {
                throw StoragePluginException(std::string("error while reading file ") + path + ": multiple objet with same name !");
            }
            throw StoragePluginException(std::string("error while reading file ") + path + ": object not found !");
        }
        else
        {
            throw StoragePluginException(std::string("error while reading file ") + path + ": " + result.GetError().GetExceptionName().c_str() + " " + result.GetError().GetMessage().c_str());
        }
    }


    void _Read(char* data, size_t size, size_t fromOffset, bool useRange)
    {
        std::string firstExceptionMessage;

        for (auto& path : paths_)
        {
            try
            {
                return __Read(path, data, size, fromOffset, useRange);
            }
            catch (StoragePluginException& ex)
            {
                if (firstExceptionMessage.empty())
                {
                    firstExceptionMessage = ex.what();
                }
                //ignore to retry
            }
        }
        throw StoragePluginException(firstExceptionMessage);
    }

    void __Read(const std::string& path, char* data, size_t size, size_t fromOffset, bool useRange)
    {
        OrthancPlugins::LogWarning(std::string("--path: ")+path);
        OrthancPlugins::LogWarning(std::string("--useRange: ") + std::to_string(useRange));
        OrthancPlugins::LogWarning(std::string("--size: ") + std::to_string(size));
        OrthancPlugins::LogWarning(std::string("--fromOffset: ") + std::to_string(fromOffset));
        Aws::S3::Model::GetObjectRequest getObjectRequest;
        getObjectRequest.SetBucket(bucketName_.c_str());
        getObjectRequest.SetKey(path.c_str());

        if (useRange)
        {
            // https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests
            //调试不设置看能取回全部数据不能
            //std::string range = std::string("bytes=") + boost::lexical_cast<std::string>(fromOffset) + "-" + boost::lexical_cast<std::string>(fromOffset + size - 1);
            //getObjectRequest.SetRange(range.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);
                OrthancPlugins::LogWarning(std::string("--写本地 ") );
                // 取回来存本地
                std::ofstream pp("d:/55.dcm", std::ios_base::out | std::ios_base::binary);
                pp.write(data, 541458);
                pp.close();

                return istream.release();
            });*/
        OrthancPlugins::LogWarning(std::string("--AWS S3 Storage plugin: DirectReader: "));
        // Get the object
        OrthancPlugins::LogWarning(std::string("--发请求 "));
        auto result = client_->GetObject(getObjectRequest);
        OrthancPlugins::LogWarning(std::string("--取请求结束 "));
        if (result.IsSuccess())
        {
            
             /*
             OrthancPlugins::LogWarning(std::string("--成功写本地55 "));
                Aws::OFStream local_file;
                local_file.open("d:/56.dcm", std::ios::out | std::ios::binary);
                local_file << result.GetResult().GetBody().rdbuf();              
                std::cout << "Done!" << std::endl;
                OrthancPlugins::LogWarning(std::string("--成功写本地结束 "));
                */
             
                auto content_length=  result.GetResult().GetContentLength();
                OrthancPlugins::LogWarning(std::string("--content_length-- ")+std::to_string(content_length));

                result.GetResult().GetBody().read(data, size);


                // 获取对象大小  
              
            
            OrthancPlugins::LogWarning(std::string("--AWS S3 Storage plugin: GetObject:IsSuccess "));  
        }
        else
        {
            OrthancPlugins::LogWarning(std::string("--AWS S3 direct read: "));
            throw StoragePluginException(std::string("error while reading file ") + path + ": response code = " + boost::lexical_cast<std::string>((int)result.GetError().GetResponseCode()) + " " + result.GetError().GetExceptionName().c_str() + " " + result.GetError().GetMessage().c_str());
        }
    }

};



class TransferWriter : public IStorage::IWriter
{
    std::string             path_;
    std::shared_ptr<Aws::Transfer::TransferManager>  transferManager_;
    std::string             bucketName_;

public:
    TransferWriter(std::shared_ptr<Aws::Transfer::TransferManager> transferManager, const std::string& bucketName, const std::string& path)
        : path_(path),
        transferManager_(transferManager),
        bucketName_(bucketName)
    {
    }

    virtual ~TransferWriter()
    {
    }

    virtual void Write(const char* data, size_t size)
    {
        boost::interprocess::bufferstream buffer(const_cast<char*>(static_cast<const char*>(data)), static_cast<size_t>(size));
        std::shared_ptr<Aws::IOStream> body = Aws::MakeShared<Aws::IOStream>(ALLOCATION_TAG, buffer.rdbuf());

        std::shared_ptr<Aws::Transfer::TransferHandle> transferHandle = transferManager_->UploadFile(body, bucketName_, path_.c_str(), "application/binary", Aws::Map<Aws::String, Aws::String>());
        transferHandle->WaitUntilFinished();

        if (transferHandle->GetStatus() != Aws::Transfer::TransferStatus::COMPLETED)
        {
            OrthancPlugins::LogWarning(std::string("--AWS S3 TransferWriter Write: "));
            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());
        }
    }
};


class TransferReader : public DirectReader
{
    std::shared_ptr<Aws::Transfer::TransferManager>  transferManager_;

public:
    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)
        : DirectReader(client, bucketName, paths, uuid),
        transferManager_(transferManager)
    {
        OrthancPlugins::LogWarning(std::string("--TransferReader c---: "));
    }

    virtual ~TransferReader()
    {

    }

    virtual void ReadWhole(char* data, size_t size)
    {
        OrthancPlugins::LogWarning(std::string("--AWS S3 TransferReader ---ReadWhole: "));
        std::string firstExceptionMessage;

        for (auto& path : paths_)
        {
            try
            {
                // The local variable 'streamBuffer' is captured by reference in a lambda.
                // It must persist until all downloading by the 'transfer_manager' is complete.
                Aws::Utils::Stream::PreallocatedStreamBuf streamBuffer(reinterpret_cast<unsigned char*>(data), size);

                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.
                    return Aws::New<Aws::IOStream>(ALLOCATION_TAG, &streamBuffer);
                    });

                downloadHandler->WaitUntilFinished();

                if (downloadHandler->GetStatus() == Aws::Transfer::TransferStatus::COMPLETED)
                {
                    return;
                }
                else if (firstExceptionMessage.empty())
                {
                    firstExceptionMessage = downloadHandler->GetLastError().GetMessage();
                }
                // 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();
                // });


            }
            catch (StoragePluginException& ex)
            {
                if (firstExceptionMessage.empty())
                {
                    firstExceptionMessage = ex.what();
                }
                //ignore to retry
            }
        }
        throw StoragePluginException(firstExceptionMessage);
    }

};




const char* AwsS3StoragePluginFactory::GetStoragePluginName()
{
    return "AWS S3 Storage";
}

const char* AwsS3StoragePluginFactory::GetStorageDescription()
{
    return "Stores the Orthanc storage area in AWS S3";
}

static std::unique_ptr<Aws::Crt::ApiHandle>  api_;
static std::unique_ptr<Aws::SDKOptions>  sdkOptions_;

#include <stdarg.h>

class AwsOrthancLogger : public Aws::Utils::Logging::LogSystemInterface
{
public:
    virtual ~AwsOrthancLogger() {}

    /**
     * Gets the currently configured log level for this logger.
     */
    virtual Aws::Utils::Logging::LogLevel GetLogLevel() const
    {
        return Aws::Utils::Logging::LogLevel::Trace;
    }
    /**
     * Does a printf style output to the output stream. Don't use this, it's unsafe. See LogStream
     */
    virtual void Log(Aws::Utils::Logging::LogLevel logLevel, const char* tag, const char* formatStr, ...)
    {
        Aws::StringStream ss;

        va_list args;
        va_start(args, formatStr);

        va_list tmp_args; //unfortunately you cannot consume a va_list twice
        va_copy(tmp_args, args); //so we have to copy it
#ifdef _WIN32
        const int requiredLength = _vscprintf(formatStr, tmp_args) + 1;
#else
        const int requiredLength = vsnprintf(nullptr, 0, formatStr, tmp_args) + 1;
#endif
        va_end(tmp_args);

        char outputBuff[999];
#ifdef _WIN32
        vsnprintf_s(outputBuff, requiredLength, _TRUNCATE, formatStr, args);
#else
        vsnprintf(outputBuff, requiredLength, formatStr, args);
#endif // _WIN32

        if (logLevel == Aws::Utils::Logging::LogLevel::Debug || logLevel == Aws::Utils::Logging::LogLevel::Trace)
        {
            LOG(INFO) << reinterpret_cast<const char*>(&outputBuff[0]);
        }
        else if (logLevel == Aws::Utils::Logging::LogLevel::Warn)
        {
            LOG(WARNING) << reinterpret_cast<const char*>(&outputBuff[0]);
        }
        else
        {
            LOG(ERROR) << reinterpret_cast<const char*>(&outputBuff[0]);
        }

        va_end(args);
    }
    /**
    * Writes the stream to the output stream.
    */
    virtual void LogStream(Aws::Utils::Logging::LogLevel logLevel, const char* tag, const Aws::OStringStream& messageStream)
    {
        if (logLevel == Aws::Utils::Logging::LogLevel::Debug || logLevel == Aws::Utils::Logging::LogLevel::Trace)
        {
            LOG(INFO) << tag << messageStream.str();
        }
        else if (logLevel == Aws::Utils::Logging::LogLevel::Warn)
        {
            LOG(WARNING) << tag << messageStream.str();
        }
        else
        {
            LOG(ERROR) << tag << messageStream.str();
        }

    }
    /**
     * Writes any buffered messages to the underlying device if the logger supports buffering.
     */
    virtual void Flush() {}
};

IStorage* AwsS3StoragePluginFactory::CreateStorage(const std::string& nameForLogs, const OrthancPlugins::OrthancConfiguration& orthancConfig)
{
    if (sdkOptions_.get() != NULL)
    {
        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, "Cannot initialize twice");
    }

    bool enableLegacyStorageStructure;
    bool storageContainsUnknownFiles;

    if (!orthancConfig.IsSection(GetConfigurationSectionName()))
    {
        OrthancPlugins::LogWarning(std::string(GetStoragePluginName()) + " plugin, section missing.  Plugin is not enabled.");
        return nullptr;
    }

    OrthancPlugins::OrthancConfiguration pluginSection;
    orthancConfig.GetSection(pluginSection, GetConfigurationSectionName());

    if (!BaseStorage::ReadCommonConfiguration(enableLegacyStorageStructure, storageContainsUnknownFiles, pluginSection))
    {
        return nullptr;
    }

    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;
    }

    const std::string endpoint = pluginSection.GetStringValue("Endpoint", "");
    const unsigned int connectTimeout = pluginSection.GetUnsignedIntegerValue("ConnectTimeout", 30);
    const unsigned int requestTimeout = pluginSection.GetUnsignedIntegerValue("RequestTimeout", 1200);
    const bool virtualAddressing = pluginSection.GetBooleanValue("VirtualAddressing", true);
    const bool enableAwsSdkLogs = pluginSection.GetBooleanValue("EnableAwsSdkLogs", false);
    const std::string caFile = orthancConfig.GetStringValue("HttpsCACertificates", "");


    api_.reset(new Aws::Crt::ApiHandle);

    sdkOptions_.reset(new Aws::SDKOptions);
    sdkOptions_->cryptoOptions.initAndCleanupOpenSSL = false;  // Done by the Orthanc framework
    sdkOptions_->httpOptions.initAndCleanupCurl = false;  // Done by the Orthanc framework

    if (enableAwsSdkLogs)
    {
        // Set up logging
        Aws::Utils::Logging::InitializeAWSLogging(Aws::MakeShared<AwsOrthancLogger>(ALLOCATION_TAG));
        // strangely, this seems to disable logging !!!! sdkOptions_->loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Trace;
    }

    Aws::InitAPI(*sdkOptions_);


    try
    {
        Aws::Client::ClientConfiguration configuration;

        configuration.region = region.c_str();
        configuration.scheme = Aws::Http::Scheme::HTTPS;
        configuration.connectTimeoutMs = connectTimeout * 1000;
        configuration.requestTimeoutMs = requestTimeout * 1000;
        configuration.httpRequestTimeoutMs = requestTimeout * 1000;

        if (!endpoint.empty())
        {
            configuration.endpointOverride = endpoint.c_str();
        }

        if (!caFile.empty())
        {
            configuration.caFile = caFile;
        }

        bool useTransferManager = false; // new in v 2.3.0
        unsigned int transferPoolSize = 10;
        unsigned int transferBufferSizeMB = 5;

        pluginSection.LookupBooleanValue(useTransferManager, "UseTransferManager");
        pluginSection.LookupUnsignedIntegerValue(transferPoolSize, "TransferPoolSize");
        pluginSection.LookupUnsignedIntegerValue(transferBufferSizeMB, "TransferBufferSize");


        std::shared_ptr<Aws::S3::S3Client> client;

        if (pluginSection.LookupStringValue(accessKey, "AccessKey") && pluginSection.LookupStringValue(secretKey, "SecretKey"))
        {
            OrthancPlugins::LogInfo("AWS S3 Storage: using credentials from the configuration file");
            Aws::Auth::AWSCredentials credentials(accessKey.c_str(), secretKey.c_str());

            client = Aws::MakeShared<Aws::S3::S3Client>(ALLOCATION_TAG, credentials, configuration, Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never, virtualAddressing);
        }
        else
        {
            // when using default credentials, credentials are not checked at startup but only the first time you try to access the bucket !
            OrthancPlugins::LogInfo("AWS S3 Storage: using default credentials provider");
            client = Aws::MakeShared<Aws::S3::S3Client>(ALLOCATION_TAG, configuration, Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never, virtualAddressing);
        }

        OrthancPlugins::LogInfo("AWS S3 storage initialized");

        return new AwsS3StoragePlugin(nameForLogs, client, bucketName, enableLegacyStorageStructure, storageContainsUnknownFiles, useTransferManager, transferPoolSize, transferBufferSizeMB);
    }
    catch (const std::exception& e)
    {
        OrthancPlugins::LogError(std::string("AWS S3 Storage plugin: failed to initialize plugin: ") + e.what());
        return nullptr;
    }

}


AwsS3StoragePlugin::~AwsS3StoragePlugin()
{
    assert(sdkOptions_.get() != NULL);
    Aws::ShutdownAPI(*sdkOptions_);
    api_.reset();
}


AwsS3StoragePlugin::AwsS3StoragePlugin(const std::string& nameForLogs,
    std::shared_ptr<Aws::S3::S3Client> client,
    const std::string& bucketName,
    bool enableLegacyStorageStructure,
    bool storageContainsUnknownFiles,
    bool useTransferManager,
    unsigned int transferThreadPoolSize,
    unsigned int transferBufferSizeMB)
    : BaseStorage(nameForLogs, enableLegacyStorageStructure),
    bucketName_(bucketName),
    storageContainsUnknownFiles_(storageContainsUnknownFiles),
    useTransferManager_(useTransferManager),
    client_(client)
{
    if (useTransferManager_)
    {
        executor_ = Aws::MakeShared<Aws::Utils::Threading::PooledThreadExecutor>(ALLOCATION_TAG, transferThreadPoolSize);
        Aws::Transfer::TransferManagerConfiguration transferConfig(executor_.get());
        transferConfig.s3Client = client_;
        transferConfig.bufferSize = static_cast<uint64_t>(transferBufferSizeMB) * 1024 * 1024;
        transferConfig.transferBufferMaxHeapSize = static_cast<uint64_t>(transferBufferSizeMB) * 1024 * 1024 * transferThreadPoolSize;

        transferManager_ = Aws::Transfer::TransferManager::Create(transferConfig);
    }
}

IStorage::IWriter* AwsS3StoragePlugin::GetWriterForObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled)
{
    if (useTransferManager_)
    {
        return new TransferWriter(transferManager_, bucketName_, GetPath(uuid, type, encryptionEnabled));
    }
    else
    {
        return new DirectWriter(client_, bucketName_, GetPath(uuid, type, encryptionEnabled));
    }
}

IStorage::IReader* AwsS3StoragePlugin::GetReaderForObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled)
{
    std::list<std::string> paths;
    paths.push_back(GetPath(uuid, type, encryptionEnabled, false));
    if (storageContainsUnknownFiles_)
    {
        paths.push_back(GetPath(uuid, type, encryptionEnabled, true));
    }

    if (useTransferManager_)
    {
        return new TransferReader(transferManager_, client_, bucketName_, paths, uuid);
    }
    else
    {
        return new DirectReader(client_, bucketName_, paths, uuid);
    }
}

void AwsS3StoragePlugin::DeleteObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled)
{
    std::string firstExceptionMessage;

    std::list<std::string> paths;
    paths.push_back(GetPath(uuid, type, encryptionEnabled, false));
    if (storageContainsUnknownFiles_)
    {
        paths.push_back(GetPath(uuid, type, encryptionEnabled, true));
    }

    // DeleteObject succeeds even if the file does not exist -> we need to try to delete every path
    for (auto& path : paths)
    {
        Aws::S3::Model::DeleteObjectRequest deleteObjectRequest;
        deleteObjectRequest.SetBucket(bucketName_.c_str());
        deleteObjectRequest.SetKey(path.c_str());

        auto result = client_->DeleteObject(deleteObjectRequest);

        if (!result.IsSuccess() && firstExceptionMessage.empty())
        {
            OrthancPlugins::LogWarning(std::string("--AWS S3 DeleteObject: "));
            firstExceptionMessage = std::string("error while deleting file ") + path + ": response code = " + boost::lexical_cast<std::string>((int)result.GetError().GetResponseCode()) + " " + result.GetError().GetExceptionName().c_str() + " " + result.GetError().GetMessage().c_str();
        }
    }

    if (!firstExceptionMessage.empty())
    {
        throw StoragePluginException(firstExceptionMessage);
    }
}

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐