- 开源支持 S3 协议的对象存储
- 支持多个客户端访问,支持分布式集群部署,容器化部署
- 考虑集成在项目中,作为后端存储进行测试
Introduction
Features
- Open source Object Storage: MINIO
- Amazon S3, Apache License v2.0
- Client/Server Mode
- Support Erasure Code
- Website: https://min.io/
- Github Repo:https://github.com/minio/minio
Details
- MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。
- MinIO是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或者 MySQL。
- [Java Client API]
- [Java Client API -zh]
- 官方测试地址:https://play.minio.io:9000
Start
Docker
docker pull minio/minio
- Run image (without access_key & secret_key)
# -it 运行参数
# -p 指定端口
# -d 后台运行
docker run -it -p 9000:9000 -d minio/minio server /data
docker run -p 9000:9000 --name minio \
-d --restart=always \
-e "MINIO_ACCESS_KEY=admin" \
-e "MINIO_SECRET_KEY=admin123456" \
-v /home/data:/data \
-v /home/config:/root/.minio \
minio/minio server /data
AWS S3 SDK for C++
Prerequisite
make -j `nproc` -C aws-cpp-sdk-core
make -j `nproc` -C aws-cpp-sdk-s3
make install DESTDIR=/c++/C-Example/linux/install -C ../build/aws-cpp-sdk-s3
make install DESTDIR=/c++/C-Example/linux/install -C ../build/aws-cpp-sdk-core
Sample Code: List Buckets
- Test Code: 使用 Minio 示例测试。
#include <iostream>
#include <aws/s3/S3Client.h>
#include <aws/core/Aws.h>
#include <aws/core/auth/AWSCredentialsProvider.h>
using namespace Aws::S3;
using namespace Aws::S3::Model;
using namespace std;
int main(int argc, char* argv[]) {
Aws::SDKOptions options;
options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Trace;
Aws::InitAPI(options);
Aws::Client::ClientConfiguration cfg;
cfg.endpointOverride = "https://play.minio.io:9000"; // S3服务器地址和端口
cfg.scheme = Aws::Http::Scheme::HTTP;
cfg.verifySSL = false;
Aws::Auth::AWSCredentials cred("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG"); // 认证的Key
S3Client client(cred, cfg, Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never, false);
auto response = client.ListBuckets();
if (response.IsSuccess()) {
auto buckets = response.GetResult().GetBuckets();
for (auto iter = buckets.begin(); iter != buckets.end(); ++iter) {
cout << iter->GetName() << "\t" << iter->GetCreationDate().ToLocalTimeString(Aws::Utils::DateFormat::ISO_8601) << endl;
}
} else {
cout << "Error while ListBuckets " << response.GetError().GetExceptionName()
<< " " << response.GetError().GetMessage() << endl;
}
Aws::ShutdownAPI(options);
return 0;
}
IDE Configuration (VS Code Remote)
- VS Code Remote 开发时可能缺少对 gcc 的相关配置,示例如下:
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/install/**"
],
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "c11",
"cppStandard": "c++14",
"intelliSenseMode": "clang-x64"
},
{
"name": "huawei-cloud",
"includePath": [
"${workspaceFolder}/install/**"
],
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "c11",
"cppStandard": "c++14",
"intelliSenseMode": "gcc-x64"
}
],
"version": 4
}
CMake Env Configuration
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo)
add_compile_options( -std=c++11 )
include_directories(/c++/C-Example/linux/install/usr/local/include)
link_directories(/c++/C-Example/linux/install/usr/local/lib64)
# 指定生成目标
add_executable(Demo object_storage.cpp)
target_link_libraries(Demo -laws-cpp-sdk-core -laws-cpp-sdk-s3)
Build And Run
cmake .
make
- make 时可能报错,缺少依赖,如 aws-c-common, aws-checksums and aws-c-event-stream. 在安装 AWS SDK 时会默认将这些依赖包放在 .deps/install/lib,如果没有安装,自行 make,make 完成之后导出
export LD_LIBRARY_PATH=/c++/C-Example/linux/.deps/install/lib64
cmake .
make
./Demo
# 输出结果 只列出部分
ezz 2020-03-17T02:13:55Z
ezz1 2020-03-17T02:19:09Z
f0569eee-6888-4d7b-a40c-bf0ee4ce65ad 2020-03-10T06:09:44Z
f0953c46-45d2-4442-b0b4-24d4e34206ef 2020-03-14T00:46:38Z
f3d50918-fee0-438a-8d9d-5c80fab1e43c 2020-03-17T19:30:46Z
flink 2020-02-25T21:00:54Z
foobar 2020-03-12T19:16:42Z
foobbar 2020-03-12T19:00:40Z
form 2020-03-09T05:47:00Z
foundation-qa 2020-03-24T00:39:02Z
j2n1g7fjsa9rjhceprqhphfktreq5hjx 2020-02-26T02:21:04Z
jalgasdlkglskadg 2020-02-29T18:27:11Z
jayce 2020-03-03T20:56:08Z
jgjhghgj 2020-03-16T00:07:13Z
jh6-gateway 2020-03-23T10:19:00Z
jianmeng2 2020-03-02T09:16:31Z
jjj 2020-03-05T23:56:21Z
jvtest 2020-03-19T23:53:31Z
jyyy-enclosure 2020-03-03T15:10:44Z
kannappan300 2020-03-19T02:55:18Z
kdyotzwd9278ke3gvukngmuqovylerxu 2020-03-20T10:49:53Z
kek 2020-03-18T06:46:26Z
kgramlkxni5sbd116ep81gz3d38gysqd 2020-03-07T05:12:23Z
kkk 2020-03-05T23:56:12Z
kopia-test-00040bab8a787438 2020-03-21T12:20:30Z
kopia-test-fe2c2dc0cc28b253 2020-03-04T12:46:37Z
mikbucket 2020-03-19T21:16:31Z
minio-backup-bucket-name-here 2020-03-26T16:38:50Z
zzz01 2020-03-16T08:02:36Z
zzz02 2020-03-16T08:02:38Z
Sample Code 2 GET/PUT
#include <iostream>
#include <aws/s3/S3Client.h>
#include <aws/core/Aws.h>
#include <aws/core/auth/AWSCredentialsProvider.h>
// Include object request header
#include <aws/s3/model/PutObjectRequest.h>
#include <aws/s3/model/GetObjectRequest.h>
#include <fstream>
using namespace Aws::S3;
using namespace Aws::S3::Model;
using namespace std;
int main(int argc, char* argv[]) {
// Set log options and init aws sdk.
Aws::SDKOptions options;
options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Trace;
Aws::InitAPI(options);
// Create the client with configuration (endpoint, protocol, ssl, credential)
Aws::Client::ClientConfiguration cfg;
cfg.endpointOverride = "http://localhost:9000"; // S3服务器地址和端口
cfg.scheme = Aws::Http::Scheme::HTTP;
cfg.verifySSL = false;
// Create credential with access key and secret key.
Aws::Auth::AWSCredentials cred("admin", "admin123456"); // 认证的Key
S3Client client(cred, cfg, Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never, false);
// Call api provided by aws sdk.
// List buckets
auto response = client.ListBuckets();
if (response.IsSuccess()) {
// Parse response
auto buckets = response.GetResult().GetBuckets();
for (auto iter = buckets.begin(); iter != buckets.end(); ++iter) {
// cout bucket name and creation date.
cout << iter->GetName() << "\t" << iter->GetCreationDate().ToLocalTimeString(Aws::Utils::DateFormat::ISO_8601) << endl;
}
} else {
// Error handle.
cout << "Error while ListBuckets " << response.GetError().GetExceptionName()
<< " " << response.GetError().GetMessage() << endl;
}
// Read Objects
// Parameters
std::string BucketName = "test";
std::string KeyName = "key";
// Local path to store the data returned by Cloud Storage
std::string PathKey = "./test-data/key.png";
// Create the object get request
Aws::S3::Model::GetObjectRequest object_get_request;
object_get_request.WithBucket(BucketName.c_str()).WithKey(KeyName.c_str());
// Send Get Request
auto get_response = client.GetObject(object_get_request);
// Verify the status of response.
if (get_response.IsSuccess())
{
// Read data and write into the file.
Aws::OFStream local_file(PathKey.c_str(), std::ios::trunc | ::ios::out | std::ios::binary);
if (local_file) {
std::cout << "File exsits!" << std::endl;
} else {
std::cout << "File not exsits!" << std::endl;
}
local_file << get_response.GetResult().GetBody().rdbuf();
if (local_file) {
std::cout << "After write, File exsits!" << std::endl;
} else {
std::cout << "After write, File not exsits!" << std::endl;
}
std::cout << "Done!" << std::endl;
}
else
{
std::cout << "GetObject error: " <<
get_response.GetError().GetExceptionName() << " " <<
get_response.GetError().GetMessage() << std::endl;
}
// Write Objects
// Parameters
std::string writeKeyName = "keyw";
// Local path to store the data returned by Cloud Storage
std::string writePathKey = "./test-data/key.png";
PutObjectRequest putObjectRequest;
putObjectRequest.WithBucket(BucketName.c_str()).WithKey(writeKeyName.c_str());
auto input_data = Aws::MakeShared<Aws::FStream>("PutObjectInputStream", writePathKey.c_str(), std::ios_base::in | std::ios_base::binary);
putObjectRequest.SetBody(input_data);
auto putObjectResult = client.PutObject(putObjectRequest);
if (putObjectResult.IsSuccess())
{
std::cout << "Upload Done!" << std::endl;
}
else
{
std::cout << "PutObject error: " <<
putObjectResult.GetError().GetExceptionName() << " " <<
putObjectResult.GetError().GetMessage() << std::endl;
}
// Deallocate AWS resources
Aws::ShutdownAPI(options);
return 0;
}
- 注意路径的格式,相对路径和绝对路径的区分,上例使用的是相对路径。
将 aws-s3-cpp-sdk 封装为 C library
Create CPP Class
Project Tree
├── CMakeLists.txt
├── include
│ ├── S3Util.h
└── src
├── CMakeLists.txt
├── main.cpp
└── S3Util.cpp
#ifndef S3UTIL_H
#define S3UTIL_H
#include <iostream>
#include <aws/s3/S3Client.h>
#include <aws/core/Aws.h>
#include <aws/core/auth/AWSCredentialsProvider.h>
// Include object request header
#include <aws/s3/model/PutObjectRequest.h>
#include <aws/s3/model/GetObjectRequest.h>
#include <aws/s3/model/CreateBucketRequest.h>
#include <fstream>
using namespace Aws::S3;
using namespace Aws::S3::Model;
using namespace std;
class S3Util
{
private:
static Aws::SDKOptions options;
static S3Client* client;
public:
S3Util();
bool createBucket(char* BucketName);
bool uploadfile(char* BucketName, char* objectKey, char* pathkey);
bool downloadfile(char* BucketName, char* objectKey, char* pathkey);
~S3Util();
};
#endif
S3Util.cpp
#include "S3Util.h"
Aws::SDKOptions S3Util::options = {};
S3Client* S3Util::client = {};
S3Util::S3Util() {
// Set log options and init aws sdk.
options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Trace;
Aws::InitAPI(options);
// Create the client with configuration (endpoint, protocol, ssl, credential)
Aws::Client::ClientConfiguration cfg;
cfg.endpointOverride = "http://114.116.234.136:9000"; // S3服务器地址和端口
cfg.scheme = Aws::Http::Scheme::HTTP;
cfg.verifySSL = false;
// Create credential with access key and secret key.
Aws::Auth::AWSCredentials cred("admin", "admin123456"); // 认证的Key
client = new S3Client(cred, cfg, Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never, false);
}
bool S3Util::createBucket(char* BucketName) {
CreateBucketRequest createBucketReq;
createBucketReq.WithBucket(BucketName);
auto createResponse = client->CreateBucket(createBucketReq);
if (createResponse.IsSuccess())
{
std::cout << "Create Bucket Done!" << std::endl;
return true;
}
else
{
std::cout << "Create Bucket error: " <<
createResponse.GetError().GetExceptionName() << " " <<
createResponse.GetError().GetMessage() << std::endl;
return false;
}
}
bool S3Util::uploadfile(char* BucketName, char* objectKey, char* pathkey) {
PutObjectRequest putObjectRequest;
putObjectRequest.WithBucket(BucketName).WithKey(objectKey);
auto input_data = Aws::MakeShared<Aws::FStream>("PutObjectInputStream", pathkey, std::ios_base::in | std::ios_base::binary);
putObjectRequest.SetBody(input_data);
auto putObjectResult = client->PutObject(putObjectRequest);
if (putObjectResult.IsSuccess())
{
std::cout << "Upload Done!" << std::endl;
return true;
}
else
{
std::cout << "PutObject error: " <<
putObjectResult.GetError().GetExceptionName() << " " <<
putObjectResult.GetError().GetMessage() << std::endl;
return false;
}
}
bool S3Util::downloadfile(char* BucketName, char* objectKey, char* pathkey) {
// Create the object get request
Aws::S3::Model::GetObjectRequest object_get_request;
object_get_request.WithBucket(BucketName).WithKey(objectKey);
// Send Get Request
auto get_response = client->GetObject(object_get_request);
// Verify the status of response.
if (get_response.IsSuccess())
{
// Read data and write into the file.
Aws::OFStream local_file(pathkey, std::ios::trunc | ::ios::out | std::ios::binary);
if (local_file) {
std::cout << "File exsits!" << std::endl;
} else {
std::cout << "File not exsits!" << std::endl;
}
local_file << get_response.GetResult().GetBody().rdbuf();
if (local_file) {
std::cout << "After write, File exsits!" << std::endl;
} else {
std::cout << "After write, File not exsits!" << std::endl;
}
std::cout << "Done!" << std::endl;
return true;
}
else
{
std::cout << "GetObject error: " <<
get_response.GetError().GetExceptionName() << " " << get_response.GetError().GetMessage() << std::endl;
return false;
}
}
S3Util::~S3Util() {
Aws::ShutdownAPI(options);
}
测试程序 main.cpp
#include "S3Util.h"
#include <iostream>
using namespace std;
int main(int argc, char* argv[]) {
std::cout << "Start!"<< std::endl;
char *BucketName = "test";
char *objectKey = "key";
char *path = "./../test-data/key.png";
S3Util* util = new S3Util();
bool result = util->downloadfile(BucketName, objectKey, path);
std::cout << "Download reslut " << result << std::endl;
std::cout << "End!"<< std::endl;
return 0;
}
CMake build
Outside CMakeLists.txt
#CMake最低版本号要求
cmake_minimum_required(VERSION 2.8)
#指定项目名称
project(s3util)
#指定版本信息
set(CMAKE_SYSTEM_VERSION 1)
#若是需要指定编译器路径
#set(CROSS_TOOLCHAIN_PREFIX "/path/arm-linux-")
#指定编译器
#set(CMAKE_C_COMPILER "${CROSS_TOOLCHAIN_PREFIX}gcc")
#set(CMAKE_CXX_COMPILER "${CROSS_TOOLCHAIN_PREFIX}g++")
#使用默认路径的g++指定编译器
#set(CMAKE_CXX_COMPILER "g++")
#指定编译选项
set(CMAKE_BUILD_TYPE Debug )
#指定编译目录
set(PROJECT_BINARY_DIR ${PROJECT_SOURCE_DIR}/build)
#添加子目录,这样进入源码文件src目录可以继续构建
add_subdirectory(${PROJECT_SOURCE_DIR}/src)
Inside CMakeLists.txt
#查找当前目录下的所有源文件,
#并将名称保存到DIR_LIB_SRCS目录
#aux_source_directory(. DIR_LIB_SRCS)
#指定头文件目录,PROJECT_SOURCE_DIR为工程的根目录
include_directories(${PROJECT_SOURCE_DIR}/include)
add_compile_options( -std=c++11 )
include_directories(/c++/C-Example/linux/install/usr/local/include)
link_directories(/c++/C-Example/linux/install/usr/local/lib64)
#指定可执行文件的输出目录,输出到bin下面
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
#生成动态库
add_library(s3_shared_demo SHARED S3Util.cpp)
#设置库输出名为 shared => libshared.so
set_target_properties(s3_shared_demo PROPERTIES OUTPUT_NAME "s3shared")
#生成静态库
add_library(s3_static_demo STATIC S3Util.cpp)
#设置输库出名为 static => libstatic.a
set_target_properties(s3_static_demo PROPERTIES OUTPUT_NAME "s3static")
#指定库文件输出路径
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
#在指定目录下查找库,并保存在LIBPATH变量中
find_library(LIBPATHS shared ${PROJECT_SOURCE_DIR}/lib)
#指定生成目标
add_executable(main main.cpp)
#链接共享库
target_link_libraries(main ${LIBPATHS})
target_link_libraries(main -laws-cpp-sdk-core -laws-cpp-sdk-s3 s3_shared_demo)
编写 Wrapper
Project Tree
├── CMakeLists.txt
├── include
│ ├── S3TestWrapper.h
└── src
├── CMakeLists.txt
├── main.cpp
└── S3Util.cpp
#ifndef S3TESTWRAPPER_H
#define S3TESTWRAPPER_H
#ifdef __cplusplus
extern "C" {
#endif
int createBucket(char* BucketName);
int uploadfile(char* BucketName, char* objectKey, char* pathkey);
int downloadfile(char* BucketName, char* objectKey, char* pathkey);
#ifdef __cplusplus
}
#endif
#endif
define wrapper
#include "S3TestWrapper.h"
#include "S3Util.h"
#ifdef __cplusplus
extern "C" {
#endif
int createBucket(char* BucketName) {
S3Util* util = new S3Util();
return util->createBucket(BucketName);
}
int uploadfile(char* BucketName, char* objectKey, char* pathkey) {
S3Util* util = new S3Util();
return util->uploadfile(BucketName, objectKey, pathkey);
}
int downloadfile(char* BucketName, char* objectKey, char* pathkey) {
S3Util* util = new S3Util();
return util->downloadfile(BucketName, objectKey, pathkey);
}
#ifdef __cplusplus
};
#endif
测试程序 main.cpp
#include "S3TestWrapper.h"
#include <iostream>
using namespace std;
int main(int argc, char* argv[]) {
std::cout << "Start!"<< std::endl;
char *BucketName = "test";
char *objectKey = "key";
char *path = "./../test-data/key.png";
bool result = downloadfile(BucketName, objectKey, path);
std::cout << "Download reslut " << result << std::endl;
std::cout << "End!"<< std::endl;
return 0;
}
Inside CMakeLists.txt
#查找当前目录下的所有源文件,
#并将名称保存到DIR_LIB_SRCS目录
aux_source_directory(. DIR_LIB_SRCS)
#指定头文件目录,PROJECT_SOURCE_DIR为工程的根目录
include_directories(${PROJECT_SOURCE_DIR}/include)
add_compile_options( -std=c++11 )
include_directories(/c++/C-Example/linux/s3util/include)
link_directories(/c++/C-Example/linux/s3util/lib)
include_directories(/c++/C-Example/linux/install/usr/local/include)
link_directories(/c++/C-Example/linux/install/usr/local/lib64)
#指定可执行文件的输出目录,输出到bin下面
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
#生成动态库
add_library(s3_shared_demo_c SHARED S3TestWrapper.cpp)
#设置库输出名为 shared => libshared.so
set_target_properties(s3_shared_demo_c PROPERTIES OUTPUT_NAME "s3sharedC")
#生成静态库
add_library(s3_static_demo_c STATIC S3TestWrapper.cpp)
#设置输库出名为 static => libstatic.a
set_target_properties(s3_static_demo_c PROPERTIES OUTPUT_NAME "s3staticC")
#指定库文件输出路径
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
#在指定目录下查找库,并保存在LIBPATH变量中
find_library(LIBPATHS shared ${PROJECT_SOURCE_DIR}/lib)
#指定生成目标
add_executable(main main.cpp)
#链接共享库
target_link_libraries(main ${LIBPATHS})
target_link_libraries(main -ls3shared -laws-cpp-sdk-core -laws-cpp-sdk-s3 s3_shared_demo_c)
#指定生成目标
add_executable(test test.c)
#链接共享库
target_link_libraries(test ${LIBPATHS})
target_link_libraries(test -ls3shared -laws-cpp-sdk-core -laws-cpp-sdk-s3 s3_shared_demo_c)
Outside CMakeLists.txt
#CMake最低版本号要求
cmake_minimum_required(VERSION 2.8)
#指定项目名称
project(s3wrapperC)
#指定版本信息
set(CMAKE_SYSTEM_VERSION 1)
#若是需要指定编译器路径
#set(CROSS_TOOLCHAIN_PREFIX "/path/arm-linux-")
#指定编译器
#set(CMAKE_C_COMPILER "${CROSS_TOOLCHAIN_PREFIX}gcc")
#set(CMAKE_CXX_COMPILER "${CROSS_TOOLCHAIN_PREFIX}g++")
#使用默认路径的g++指定编译器
#set(CMAKE_CXX_COMPILER "g++")
#指定编译选项
set(CMAKE_BUILD_TYPE Debug )
#指定编译目录
set(PROJECT_BINARY_DIR ${PROJECT_SOURCE_DIR}/build)
#添加子目录,这样进入源码文件src目录可以继续构建
add_subdirectory(${PROJECT_SOURCE_DIR}/src)
使用 C 语言测试 main.c
main.c
#include "S3TestWrapper.h"
#include<stdio.h>
int main(int argc, char* argv[]) {
printf("start!\n");
char *BucketName = "test";
char *objectKey = "key";
char *path = "./../test-data/key-test.png";
int result = downloadfile(BucketName, objectKey, path);
printf("Download Result: %d!\n", result);
printf("end!\n");
return 0;
}
CMakeLists.txt
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo)
add_compile_options( -std=c++11 )
include_directories(/c++/C-Example/linux/install/usr/local/include)
link_directories(/c++/C-Example/linux/install/usr/local/lib64)
include_directories(/c++/C-Example/linux/s3util/include)
link_directories(/c++/C-Example/linux/s3util/lib)
include_directories(/c++/C-Example/linux/s3-c/test/include)
link_directories(/c++/C-Example/linux/s3-c/test/lib)
# 指定生成目标
add_executable(Demo main.c)
target_link_libraries(Demo -laws-cpp-sdk-core -laws-cpp-sdk-s3 -ls3shared -ls3sharedC)
集成 TCMU
参考资料