Qtの応用 - AWS S3

提供: MochiuWiki : SUSE, EC, PCB

2025年11月21日 (金) 14:27時点におけるWiki (トーク | 投稿記録)による版 (アップロードフロー)

概要



AWS SDK for C++のインストール

Qtの応用_-_AWS#AWS_SDK_for_C++のインストールのページを参照すること。


S3へのアップロード

ローカルファイルをS3バケットにストリーム経由でアップロードするフローを以下に示す。

  1. ClientConfigurationの作成
    AWS SDKのクライアント設定オブジェクトを生成する。
  2. リージョンの設定
    指定されたAWSリージョンを設定する。
  3. S3クライアントの生成


S3クライアントの初期化

指定されたリージョンでS3クライアントを初期化する。

 QString region = "<リージョン名>"
 
 Aws::Client::ClientConfiguration clientConfig;
 clientConfig.region = region.toStdString();
 auto s3Client = std::make_unique<Aws::S3::S3Client>(clientConfig);


アップロードフロー

  • bucketName
    アップロード先のS3バケット名
  • objectKey
    S3上でのオブジェクトキー (パス)
  • filePath
    アップロードするローカルファイルのパス


 // 1. ファイル存在チェック
 QFileInfo fileInfo(filePath);
 if (!fileInfo.exists()) {
   return false;
 }
 
 // 2. PutObjectRequestの設定
 Aws::S3::Model::PutObjectRequest request;
 request.SetBucket(bucketName.toStdString());
 request.SetKey(objectKey.toStdString());
 
 // 3. ファイルストリームの作成
 // Aws::MakeSharedは、AWS SDK専用のスマートポインタ
 auto inputData = Aws::MakeShared<Aws::FStream>(
    "PutObjectInputStream", 
    filePath.toStdString().c_str(), 
    std::ios_base::in | std::ios_base::binary  // std::ios_base::binary でバイナリファイルにも対応
 );
 
 // 4. リクエストにデータを設定
 request.SetBody(inputData);
 request.SetContentLength(fileInfo.size());  // ファイルサイズを明示的に設定 (Content-Length)
 
 // 5. アップロード実行
 auto outcome = s3Client->PutObject(request);
 if (outcome.IsSuccess()) {  // outcome.IsSuccessメソッドは、成功 / 失敗を判定することができる
    std::cout << "✓ アップロード成功" << std::endl;
    return true;
 }
 else {
    std::cerr << "✗ アップロード失敗: " << outcome.GetError().GetMessage() << std::endl;
    return false;
 }



S3へのダウンロード



サンプルコード

以下の例では、AWS S3へのファイルアップロードとダウンロードを行っている。
全ての設定 (リージョン、バケット名、オブジェクトキー) を任意の設定ファイルから読み込んでいる。

 # config.iniファイル
 
 [AWS]
 # AWSリージョン (必須)
 region=us-east-1
 
 [S3]
 # S3バケット名 (必須)
 bucket=my-bucket-name
 
 [Upload]
 # アップロード時のS3オブジェクトキー
 object_key=uploads/myfile.txt
 
 # アップロードするローカルファイルのパス
 local_file=/path/to/local/file.txt
 
 [Download]
 # ダウンロードするS3オブジェクトキー
 object_key=downloads/myfile.txt
 
 # ダウンロード先のローカルファイルパス
 local_file=/path/to/save/file.txt


 # CMakeLists.txtファイル
 
 cmake_minimum_required(VERSION 3.16)
 project(<プロジェクト名> VERSION 1.0 LANGUAGES CXX)
 
 set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
 # Qt設定
 set(CMAKE_AUTOMOC ON)
 set(CMAKE_AUTORCC ON)
 set(CMAKE_AUTOUIC ON)
 
 # Qtの検索
 find_package(Qt6 COMPONENTS Core QUIET)
 if (NOT Qt6_FOUND)
    find_package(Qt5 5.15 REQUIRED COMPONENTS Core)
 endif()
 
 # AWS SDK for C++の検索
 find_package(AWSSDK REQUIRED COMPONENTS s3)
 
 # 実行ファイル
 add_executable(<プロジェクト名>
    main.cpp
 )
 
 # Qt6とQt5の両方に対応
 if (Qt6_FOUND)
    target_link_libraries(<プロジェクト名>
       Qt6::Core
       ${AWSSDK_LINK_LIBRARIES}
    )
 else()
    target_link_libraries(<プロジェクト名>
       Qt5::Core
       ${AWSSDK_LINK_LIBRARIES}
    )
 endif()
 
 # インストール
 install(TARGETS <プロジェクト名>
    RUNTIME DESTINATION bin
 )


 #include <QCoreApplication>
 #include <QCommandLineParser>
 #include <QSettings>
 #include <QFile>
 #include <QFileInfo>
 #include <QDebug>
 #include <fstream>
 #include <iostream>
 #include <aws/core/Aws.h>
 #include <aws/s3/S3Client.h>
 #include <aws/s3/model/PutObjectRequest.h>
 #include <aws/s3/model/GetObjectRequest.h>
 
 // 設定情報を保持する構造体
 struct S3Config {
    QString region;
    QString bucket;
    QString uploadObjectKey;
    QString uploadLocalFile;
    QString downloadObjectKey;
    QString downloadLocalFile;
 };
 
 // 設定ファイルを読み込む関数
 bool loadConfig(const QString& configPath, S3Config& config)
 {
    QFileInfo fileInfo(configPath);
    if (!fileInfo.exists()) {
       std::cerr << "エラー: 設定ファイルが見つかりません: " << configPath.toStdString() << std::endl;
       return false;
    }
 
    QSettings settings(configPath, QSettings::IniFormat);
    settings.setIniCodec("UTF-8");
 
    // AWS設定
    settings.beginGroup("AWS");
    config.region = settings.value("region").toString();
    settings.endGroup();
 
    // S3設定
    settings.beginGroup("S3");
    config.bucket = settings.value("bucket").toString();
    settings.endGroup();
 
    // アップロード設定
    settings.beginGroup("Upload");
    config.uploadObjectKey = settings.value("object_key").toString();
    config.uploadLocalFile = settings.value("local_file").toString();
    settings.endGroup();
 
    // ダウンロード設定
    settings.beginGroup("Download");
    config.downloadObjectKey = settings.value("object_key").toString();
    config.downloadLocalFile = settings.value("local_file").toString();
    settings.endGroup();
 
    // 必須項目のチェック
    if (config.region.isEmpty()) {
       std::cerr << "エラー: [AWS] region が設定されていません" << std::endl;
       return false;
    }
    if (config.bucket.isEmpty()) {
       std::cerr << "エラー: [S3] bucket が設定されていません" << std::endl;
       return false;
    }
 
    std::cout << "設定ファイル読み込み完了: " << configPath.toStdString() << std::endl;
    std::cout << "  リージョン: " << config.region.toStdString() << std::endl;
    std::cout << "  バケット名: " << config.bucket.toStdString() << std::endl;
 
    return true;
 }
 
 class S3Manager {
 private:
    std::unique_ptr<Aws::S3::S3Client> m_s3Client;
 
 public:
    S3Manager(const QString& region)
    {
       Aws::Client::ClientConfiguration clientConfig;
       clientConfig.region = region.toStdString();
       m_s3Client = std::make_unique<Aws::S3::S3Client>(clientConfig);
 
       std::cout << "S3クライアント初期化完了 (リージョン: " << region.toStdString() << ")" << std::endl;
    }
 
    bool uploadFile(const QString& bucketName, const QString& objectKey, const QString& filePath)
    {
       std::cout << "\n=== アップロード開始 ===" << std::endl;
       std::cout << "  ローカルファイル: " << filePath.toStdString() << std::endl;
       std::cout << "  S3パス: s3://" << bucketName.toStdString()  << "/" << objectKey.toStdString() << std::endl;
 
       // ファイルの存在チェック
       QFileInfo fileInfo(filePath);
       if (!fileInfo.exists()) {
          std::cerr << "エラー: ファイルが見つかりません: " << filePath.toStdString() << std::endl;
          return false;
       }
 
       Aws::S3::Model::PutObjectRequest request;
       request.SetBucket(bucketName.toStdString());
       request.SetKey(objectKey.toStdString());
 
       // ファイルをストリームとして読み込む
       auto inputData = Aws::MakeShared<Aws::FStream>("PutObjectInputStream", filePath.toStdString().c_str(), std::ios_base::in | std::ios_base::binary);
 
       if (!inputData->is_open()) {
          std::cerr << "エラー: ファイルを開けません: " << filePath.toStdString() << std::endl;
          return false;
       }
 
       request.SetBody(inputData);
       request.SetContentLength(fileInfo.size());
 
       auto outcome = m_s3Client->PutObject(request);
 
       if (outcome.IsSuccess()) {
          std::cout << "✓ アップロード成功!" << std::endl;
          return true;
       }
       else {
          std::cerr << "✗ アップロード失敗: " << outcome.GetError().GetMessage() << std::endl;
          return false;
       }
    }
 
    bool downloadFile(const QString& bucketName, const QString& objectKey, const QString& filePath)
    {
       std::cout << "\n=== ダウンロード開始 ===" << std::endl;
       std::cout << "  S3パス: s3://" << bucketName.toStdString() << "/" << objectKey.toStdString() << std::endl;
       std::cout << "  保存先: " << filePath.toStdString() << std::endl;
 
       Aws::S3::Model::GetObjectRequest request;
       request.SetBucket(bucketName.toStdString());
       request.SetKey(objectKey.toStdString());
 
       auto outcome = m_s3Client->GetObject(request);
 
       if (outcome.IsSuccess()) {
          auto& retrieved_file = outcome.GetResultWithOwnership().GetBody();
 
          std::ofstream output_file(filePath.toStdString(), std::ios::binary);
          if (!output_file.is_open()) {
             std::cerr << "エラー: 保存先ファイルを開けません: " << filePath.toStdString() << std::endl;
             return false;
          }
 
          output_file << retrieved_file.rdbuf();
          output_file.close();
 
          // ファイルサイズを表示
          QFileInfo fileInfo(filePath);
          std::cout << "✓ ダウンロード成功! (サイズ: " << fileInfo.size() << " bytes)" << std::endl;
          return true;
       }
       else {
          std::cerr << "✗ ダウンロード失敗: " << outcome.GetError().GetMessage() << std::endl;
          return false;
       }
    }
 };
 
 int main(int argc, char *argv[])
 {
    QCoreApplication app(argc, argv);
    QCoreApplication::setApplicationName("AWS S3 Manager");
    QCoreApplication::setApplicationVersion("1.0");
 
    // コマンドライン引数のパーサーを設定
    QCommandLineParser parser;
    parser.setApplicationDescription("AWS S3ファイルアップロード・ダウンロードツール (設定ファイルベース)");
    parser.addHelpOption();
    parser.addVersionOption();
 
    // オプション定義
    QCommandLineOption uploadOption(QStringList() << "u" << "upload", "ファイルをアップロード");
    parser.addOption(uploadOption);
 
    QCommandLineOption downloadOption(QStringList() << "d" << "download", "ファイルをダウンロード");
    parser.addOption(downloadOption);
 
    QCommandLineOption configOption(QStringList() << "c" << "config",  "設定ファイルのパス (デフォルト: config.ini)", "config", "config.ini");
    parser.addOption(configOption);
 
    parser.process(app);
 
    // AWS SDKの初期化
    Aws::SDKOptions options;
    Aws::InitAPI(options);
 
    int result = 0;
 
    {
       // 設定ファイルのパスを取得
       QString configPath = parser.value(configOption);
 
       // 設定を読み込む
       S3Config config;
       if (!loadConfig(configPath, config)) {
          std::cerr << "\n設定ファイルの例:" << std::endl;
          std::cerr << "  [AWS]" << std::endl;
          std::cerr << "  region=ap-northeast-1" << std::endl;
          std::cerr << "  " << std::endl;
          std::cerr << "  [S3]" << std::endl;
          std::cerr << "  bucket=my-bucket-name" << std::endl;
          std::cerr << "  " << std::endl;
          std::cerr << "  [Upload]" << std::endl;
          std::cerr << "  object_key=folder/myfile.txt" << std::endl;
          std::cerr << "  local_file=/path/to/local/file.txt" << std::endl;
          std::cerr << "  " << std::endl;
          std::cerr << "  [Download]" << std::endl;
          std::cerr << "  object_key=folder/myfile.txt" << std::endl;
          std::cerr << "  local_file=/path/to/save/file.txt" << std::endl;
          Aws::ShutdownAPI(options);
          return 1;
       }
 
       // S3マネージャーを初期化
       S3Manager s3Manager(config.region);
 
       if (parser.isSet(uploadOption)) {
          // アップロード
          if (config.uploadObjectKey.isEmpty() || config.uploadLocalFile.isEmpty()) {
             std::cerr << "エラー: [Upload] セクションに object_key と local_file が必要です" << std::endl;
             result = 1;
          }
          else {
             if (!s3Manager.uploadFile(config.bucket, config.uploadObjectKey, config.uploadLocalFile)) {
                result = 1;
             }
          }
       }
       else if (parser.isSet(downloadOption)) {
          // ダウンロード
          if (config.downloadObjectKey.isEmpty() || config.downloadLocalFile.isEmpty()) {
             std::cerr << "エラー: [Download] セクションに object_key と local_file が必要です" << std::endl;
             result = 1;
          }
          else {
             if (!s3Manager.downloadFile(config.bucket, config.downloadObjectKey, config.downloadLocalFile)) {
                result = 1;
             }
          }
       }
       else {
          std::cerr << "エラー: --upload または --download を指定してください" << std::endl;
          parser.showHelp(1);
       }
    }
 
    // AWS SDKのクリーンアップ
    Aws::ShutdownAPI(options);
 
    return result;
 }