使用AWS SDK构建一个C++语言访问S3的演示应用

一、背景

本文演示了使用AWS SDK构建一个C++语言的测试应用,应用程序执行listBucket的操作列出存储桶目录。(前提是AKSK对应的用户有权限)

二、安装SDK(动态链接库)

1、创建操作系统

海外区域创建一个Cloud9系统,选择Amazon Linux 2系统。连接类型选择System Manager或者SSH均可。

创建完毕,进入Cloud9的Shell。

2、安装开发工具

安装依存包:

sudo yum install -y gcc cmake3 git libcurl-devel openssl-devel libuuid-devel pulseaudio-libs-devel

修改cmake从默认的2.8改为3.2

sudo mv /bin/cmake /bin/cmake2
sudo ln -s /bin/cmake3 /bin/cmake

3、构建SDK(动态链接库)

git clone --recurse-submodules https://github.com/aws/aws-sdk-cpp

在当前目录下获得aws-sdk-cpp目录。

mkdir sdk_build
cd sdk_build
cmake ../aws-sdk-cpp -DCMAKE_BUILD_TYPE=Debug -DCMAKE_PREFIX_PATH=/usr/local/ -DCMAKE_INSTALL_PREFIX=/usr/local/ -DBUILD_ONLY="s3"
make
sudo make install

SDK会被安装到如下目录:(节选部分目录)

/usr/local/include/aws/
/usr/local/include/aws/s3
/usr/local/include/aws/core/

构建SDK完成。

三、构建测试程序

1、准备源代码

在当前目录下,创建一个src目录,里边包含两个文件分别是main.cppCMakeLists.txt

其中main.cpp内容如下:

#include <aws/core/Aws.h>
#include <aws/core/utils/logging/LogLevel.h>
#include <aws/s3/S3Client.h>
#include <iostream>

using namespace Aws;

int main()
{
    //The Aws::SDKOptions struct contains SDK configuration options.
    //An instance of Aws::SDKOptions is passed to the Aws::InitAPI and 
    //Aws::ShutdownAPI methods.  The same instance should be sent to both methods.
    SDKOptions options;
    options.loggingOptions.logLevel = Utils::Logging::LogLevel::Debug;
    
    //The AWS SDK for C++ must be initialized by calling Aws::InitAPI.
    InitAPI(options); 
    {
        S3::S3Client client;

        auto outcome = client.ListBuckets();
        if (outcome.IsSuccess()) {
            std::cout << "Found " << outcome.GetResult().GetBuckets().size() << " buckets\n";
            for (auto&& b : outcome.GetResult().GetBuckets()) {
                std::cout << b.GetName() << std::endl;
            }
        }
        else {
            std::cout << "Failed with error: " << outcome.GetError() << std::endl;
        }
    }

    //Before the application terminates, the SDK must be shut down. 
    ShutdownAPI(options);
    return 0;
}

文件CMakeLists.txt的内容如下:

# Minimal CMakeLists.txt for the AWS SDK for C++.
cmake_minimum_required(VERSION 3.3)
set(CMAKE_CXX_STANDARD 11)
project(app LANGUAGES CXX)

# Use shared libraries
set(BUILD_SHARED_LIBS ON CACHE STRING "Link to shared libraries by default.")

#Include in any AWS service packages your code will be using by service name
find_package(AWSSDK REQUIRED COMPONENTS s3)
add_executable(${PROJECT_NAME} "main.cpp") #add app's main sourcefile

set_compiler_flags(${PROJECT_NAME})
set_compiler_warnings(${PROJECT_NAME})
target_link_libraries(${PROJECT_NAME} ${AWSSDK_LINK_LIBRARIES})

创建完毕,可看到src目录下有如下两个文件:

-rw-r--r-- 1 ec2-user ec2-user 2245 Jul 25 14:33 CMakeLists.txt
-rw-r--r-- 1 ec2-user ec2-user 1154 Jul 25 14:32 main.cpp

2、准备构建目录

进入src目录,在其中创建编译目录。

cd src/
ls -l .
mkdir my_project_build/
cd my_project_build/

执行如下命令,编译构建可执行文件。

cmake ../
make

执行完毕后,输出结果如下:

Scanning dependencies of target app
[ 50%] Building CXX object CMakeFiles/app.dir/main.cpp.o
[100%] Linking CXX executable app
[100%] Built target app

3、执行程序

编译完毕,在当前目录下获得可执行文件,文件名为app。使用ls -l可查看如下。

total 164
-rwxrwxr-x 1 ec2-user ec2-user 131424 Jul 25 14:43 app
-rw-rw-r-- 1 ec2-user ec2-user  17873 Jul 25 14:43 CMakeCache.txt
drwxrwxr-x 6 ec2-user ec2-user    319 Jul 25 14:43 CMakeFiles
-rw-rw-r-- 1 ec2-user ec2-user   1541 Jul 25 14:43 cmake_install.cmake
-rw-rw-r-- 1 ec2-user ec2-user   4793 Jul 25 14:43 Makefile

可看到这个编译后的可执行文件体积约129KB。这是由于采用的动态编译,因此主程序很小。执行app程序测试。

./app

即可看到整个程序列出本账号下所有S3存储桶。(前提是本机配置了AWSCLI的Access Key和Secret Key)

4、查看其依赖的动态链接库:

执行如下命令:

ldd ./app

返回结果如下:

        linux-vdso.so.1 (0x00007ffe38b0b000)
        libaws-cpp-sdk-s3.so => /usr/local/lib64/libaws-cpp-sdk-s3.so (0x00007f797968c000)
        libaws-cpp-sdk-core.so => /usr/local/lib64/libaws-cpp-sdk-core.so (0x00007f7978bf0000)
        libcrypto.so.10 => /lib64/libcrypto.so.10 (0x00007f7978799000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007f7978595000)
        librt.so.1 => /lib64/librt.so.1 (0x00007f797838d000)
        libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f797800b000)
        libm.so.6 => /lib64/libm.so.6 (0x00007f7977ccb000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f7977ab5000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f7977897000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f79774ea000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f7979c6a000)
        libcurl.so.4 => /lib64/libcurl.so.4 (0x00007f7977248000)
        libz.so.1 => /lib64/libz.so.1 (0x00007f7977033000)
        libnghttp2.so.14 => /lib64/libnghttp2.so.14 (0x00007f7976e0c000)
        libidn2.so.0 => /lib64/libidn2.so.0 (0x00007f7976bbd000)
        libssh2.so.1 => /lib64/libssh2.so.1 (0x00007f7976994000)
        libssl.so.10 => /lib64/libssl.so.10 (0x00007f7976725000)
        libgssapi_krb5.so.2 => /lib64/libgssapi_krb5.so.2 (0x00007f79764d9000)
        libldap-2.4.so.2 => /lib64/libldap-2.4.so.2 (0x00007f7976287000)
        liblber-2.4.so.2 => /lib64/liblber-2.4.so.2 (0x00007f7976078000)
        libunistring.so.0 => /lib64/libunistring.so.0 (0x00007f7975d60000)
        libkrb5.so.3 => /lib64/libkrb5.so.3 (0x00007f7975a7c000)
        libcom_err.so.2 => /lib64/libcom_err.so.2 (0x00007f7975878000)
        libk5crypto.so.3 => /lib64/libk5crypto.so.3 (0x00007f7975647000)
        libkrb5support.so.0 => /lib64/libkrb5support.so.0 (0x00007f7975438000)
        libkeyutils.so.1 => /lib64/libkeyutils.so.1 (0x00007f7975234000)
        libresolv.so.2 => /lib64/libresolv.so.2 (0x00007f797501e000)
        libsasl2.so.3 => /lib64/libsasl2.so.3 (0x00007f7974e02000)
        libssl3.so => /lib64/libssl3.so (0x00007f7974ba1000)
        libsmime3.so => /lib64/libsmime3.so (0x00007f797497b000)
        libnss3.so => /lib64/libnss3.so (0x00007f797464e000)
        libnssutil3.so => /lib64/libnssutil3.so (0x00007f797441f000)
        libplds4.so => /lib64/libplds4.so (0x00007f797421b000)
        libplc4.so => /lib64/libplc4.so (0x00007f7974016000)
        libnspr4.so => /lib64/libnspr4.so (0x00007f7973dd9000)
        libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f7973bb2000)
        libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007f797397b000)
        libpcre.so.1 => /lib64/libpcre.so.1 (0x00007f7973719000)

查看其中的关键文件,其中一部分文件体积如下:

-rwxr-xr-x 1 root root 30M Jul 25 14:19 /usr/local/lib64/libaws-cpp-sdk-core.so
-rwxr-xr-x 1 root root 12M Jul 25 14:19 /usr/local/lib64/libaws-cpp-sdk-s3.so
lrwxrwxrwx 1 root root 19 Jun 21 04:48 /lib64/libcrypto.so.10 -> libcrypto.so.1.0.2k
-rwxr-xr-x 1 root root 2471392 Jun  1 17:09 /lib64/libcrypto.so.1.0.2k

这个动态链接库体积约为12MB+30MB+2MB。在其他EC2部署环境上,如果也是Amazon Linux 2系统给,只需要这几个动态链接库文件,即可支持程序运行(前提是AKSK配置正确)

为了让代码在任何一个没有以上动态链接库的环境上运行,可考虑采用静态编译。但缺点是文件体积会较大,并且更新程序时候不方便单独更新库文件。因此建议使用动态编译。

四、参考文档

AWS SDK for C++语言:

https://github.com/aws/aws-sdk-cpp

如何构建AWS SDK for C++(仅S3模块)

https://docs.aws.amazon.com/zh_cn/sdk-for-cpp/v1/developer-guide/setup-linux.html

如何构建C++的S3测试程序(功能为列出所有Bucket)

https://docs.aws.amazon.com/zh_cn/sdk-for-cpp/v1/developer-guide/build-cmake.html

更多S3操作的C++代码例子:

https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/cpp/example_code/s3