You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AWS-C++上传5GB以上S3文件速度极慢且失败问题排查

问题描述

Windows环境下基于AWS-C++ SDK开发的S3上传代码,在上传小于5GB的文件时速度正常且能完成;但上传5GB以上大文件时,速度仅约0.4MB/s,且持续报错Error: PutObject: Encountered network error when sending http request。浏览器上传同一份大文件速度正常,存储桶已开启传输加速,但不清楚如何在SDK客户端配置该功能,需排查文件大小差异导致异常的原因。


核心原因分析

  • 单请求上传的硬限制:S3的PutObject接口单请求最大仅支持5GB文件上传,超过该阈值后,SDK虽会尝试处理,但单请求传输大文件会导致内存占用过高、网络超时风险陡增,直接引发速度暴跌和网络错误。
  • 未启用分段上传:AWS-C++ SDK默认不会自动对大文件启用分段上传,而分段上传是S3处理大文件的标准方案,能通过并发传输提升速度、降低超时概率。
  • 传输加速未配置:存储桶已开启传输加速,但客户端未对应配置,无法利用AWS全球加速节点优化大文件传输路径。
  • Windows文件流效率问题:代码中使用的Aws::FStream在Windows环境下处理超大文件时,可能存在读取效率瓶颈,间接拖慢上传速度。

解决方案

1. 改用TransferManager处理大文件上传

AWS-C++ SDK的TransferManager是专门针对大文件传输的组件,会自动对超过阈值的文件启用分段上传,默认阈值为8MB,还支持自动重试、并发传输分段,大幅提升大文件上传的速度和稳定性。

2. 配置S3传输加速

在客户端配置中开启useAccelerateEndpoint参数,配合已开启加速的存储桶,让客户端自动连接到S3的全球加速节点。

3. 优化客户端与文件读取配置

  • 调整超时时间:大文件分段上传需要更长的超时窗口,建议将requestTimeoutMs设为300000毫秒(5分钟)、connectTimeoutMs设为60000毫秒(1分钟)。
  • 提升并发数:在TransferManager中设置maxUploadConcurrency(如5),同时可根据网络带宽调整分段大小(如16MB/段)。
  • 优化文件读取:Windows环境下可通过调整文件流缓冲区大小提升读取效率。

修改后的代码示例

FileHandler.h

#ifndef BEASTBURST_FILEHANDLER_H
#define BEASTBURST_FILEHANDLER_H
#include <iostream>
#include <fstream>
#include <aws/core/Aws.h>
#include <aws/s3/S3Client.h>
#include <aws/transfer/TransferManager.h>
#include <aws/core/utils/memory/stl/AWSStreamFwd.h>

namespace BeastBurstCLI
{
    class FileHandler {
    public:
        FileHandler();
        ~FileHandler();
        bool UploadFile(const Aws::String &bucketName,
                       const Aws::String &filePath,
                       const Aws::String &destinationPath);
    private:
        Aws::SDKOptions options;
        std::shared_ptr<Aws::Client::ClientConfiguration> config_;
        std::shared_ptr<Aws::Transfer::TransferManager> transferManager_;
        std::mutex gMutex;
        bool gUploadComplete = false;
        long long gBytesSent = 0;
        long long gTotalBytes = 0;
        std::chrono::steady_clock::time_point startTime;

        void OnTransferProgress(const Aws::Transfer::TransferManager*, const Aws::Transfer::TransferProgress& progress);
    };
}

#endif //BEASTBURST_FILEHANDLER_H

FileHandler.cpp

#include "FileHandler.h"
#include <iostream>
#include <iomanip>
#include <aws/s3/model/ServerSideEncryption.h>

BeastBurstCLI::FileHandler::FileHandler() : options{}
{
    options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Trace;
    Aws::InitAPI(options);
    
    // 初始化客户端配置,启用传输加速
    config_ = std::make_shared<Aws::Client::ClientConfiguration>();
    config_->region = Aws::Region::EU_CENTRAL_1;
    config_->useAccelerateEndpoint = true;
    // 调整超时时间适配大文件上传
    config_->requestTimeoutMs = 300000; // 5分钟
    config_->connectTimeoutMs = 60000; // 1分钟
    
    // 创建TransferManager配置,设置并发数和分段大小
    Aws::Transfer::TransferManagerConfiguration tmConfig;
    tmConfig.s3Client = Aws::MakeShared<Aws::S3::S3Client>("S3Client", config_);
    tmConfig.maxUploadConcurrency = 5; // 并发上传5个分段
    tmConfig.uploadChunkSize = 16 * 1024 * 1024; // 每个分段16MB,可根据网络调整
    
    transferManager_ = Aws::Transfer::TransferManager::Create(tmConfig);
}

BeastBurstCLI::FileHandler::~FileHandler()
{
    Aws::ShutdownAPI(options);
}

void BeastBurstCLI::FileHandler::OnTransferProgress(const Aws::Transfer::TransferManager*, const Aws::Transfer::TransferProgress& progress)
{
    std::lock_guard<std::mutex> lock(gMutex);
    gBytesSent = progress.GetBytesTransferred();
}

bool BeastBurstCLI::FileHandler::UploadFile(const Aws::String &bucketName, const Aws::String &filePath,
                                           const Aws::String &destinationPath) {
    gUploadComplete = false;
    gBytesSent = 0;
    
    // 获取文件大小
    std::ifstream file(filePath, std::ios::binary | std::ios::ate);
    if (!file.is_open()) {
        std::cerr << "无法打开文件: " << filePath << std::endl;
        return false;
    }
    gTotalBytes = file.tellg();
    file.close();
    
    // 构造上传请求
    Aws::Transfer::TransferRequest request;
    request.SetBucket(bucketName);
    Aws::String fileName = Aws::Utils::StringUtils::Split(filePath, '/').back();
    Aws::String objectKey = destinationPath + "/" + fileName;
    request.SetKey(objectKey);
    request.SetFilePath(filePath);
    // 设置服务器端加密
    request.SetServerSideEncryption(Aws::S3::Model::ServerSideEncryption::AES256);
    
    // 设置进度回调
    auto progressCallback = [this](const Aws::Transfer::TransferManager* tm, const Aws::Transfer::TransferProgress& progress) {
        OnTransferProgress(tm, progress);
    };
    request.SetProgressCallback(progressCallback);
    
    startTime = std::chrono::steady_clock::now();
    
    // 启动进度显示线程
    std::thread progressThread([this]() {
        while (!gUploadComplete) {
            std::this_thread::sleep_for(std::chrono::seconds(2));
            std::lock_guard<std::mutex> lock(gMutex);
            double progressMB = gBytesSent / (1024.0 * 1024.0);
            double totalMB = gTotalBytes / (1024.0 * 1024.0);
            
            std::chrono::steady_clock::time_point currentTime = std::chrono::steady_clock::now();
            std::chrono::duration<double> elapsedSeconds = currentTime - startTime;
            double uploadSpeed = progressMB / elapsedSeconds.count();
            
            if (gTotalBytes > 0 && progressMB > 0) {
                std::cout << "进度: " << (int)(progressMB * 100.0 / totalMB) << "% (" << (int)progressMB << "MB / " << (int)totalMB << "MB), ";
                std::cout << "速度: " << std::fixed << std::setprecision(2) << uploadSpeed << " MB/s" << std::endl;
            }
        }
    });
    
    // 执行上传
    auto future = transferManager_->UploadFile(request);
    auto outcome = future.get();
    
    gUploadComplete = true;
    progressThread.join();
    
    if (!outcome.IsSuccess()) {
        std::cerr << "上传失败: " << outcome.GetError().GetMessage() << std::endl;
        return false;
    } else {
        std::cout << "文件 '" << fileName << "' 已成功上传到存储桶 '" << bucketName << "' 的路径 '" << destinationPath << "'。" << std::endl;
        return true;
    }
}

额外注意事项

  • 确保使用最新版本的AWS-C++ SDK,旧版本可能在TransferManager或传输加速支持上存在缺陷。
  • 检查IAM权限,确保上传账号拥有s3:PutObject及s3:PutObjectTagging(分段上传必需)的权限。
  • 若网络环境不稳定,可在TransferManager中配置重试策略,增加重试次数提升容错性。

内容的提问来源于stack exchange,提问作者Venelin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 01:35:26