在嵌入式设备的开发中,有时候需要将文件上传到S3存储服务。为了让用户能实时了解上传进度,就需要实现上传进度回调功能。接下来,咱们就详细探讨一下如何开发这样的回调函数。

一、应用场景

嵌入式设备在很多场景下都需要将本地文件上传到云端存储,比如监控摄像头设备,它会定期将录制的视频文件上传到S3存储桶,以便后续查看和分析。在这个过程中,用户可能希望实时了解上传进度,比如上传了百分之多少,还剩多少时间等。另外,工业物联网中的传感器设备也会将采集到的数据文件上传到S3,实时的上传进度显示可以让管理员及时掌握设备的工作状态。

二、技术优缺点

优点

  1. 提升用户体验:实时显示上传进度能让用户清楚地知道文件上传的情况,避免用户因为长时间等待而产生焦虑,提升用户对设备和系统的满意度。
  2. 便于故障排查:如果上传过程中出现问题,通过实时进度可以快速定位问题所在,比如上传进度一直停滞不前,可能是网络问题或者设备出现故障。
  3. 资源管理优化:根据上传进度,可以合理安排设备的其他任务,避免在上传大文件时同时进行其他高负载操作,导致设备性能下降。

缺点

  1. 增加开发复杂度:实现进度回调功能需要额外的代码来跟踪上传进度,并将进度信息传递给显示界面,这增加了开发的难度和工作量。
  2. 性能开销:实时获取和更新上传进度会占用一定的系统资源,尤其是在嵌入式设备资源有限的情况下,可能会对设备的性能产生一定的影响。

三、实现步骤

1. 环境准备

首先,我们要确保嵌入式设备上已经安装了C++开发环境,并且可以访问S3存储服务。这里我们使用AWS SDK for C++来实现文件上传功能。以下是安装AWS SDK for C++的示例命令(假设使用的是Linux系统):

# 克隆AWS SDK for C++仓库
git clone https://github.com/aws/aws-sdk-cpp.git
cd aws-sdk-cpp
# 创建构建目录
mkdir build
cd build
# 配置和编译
cmake .. -DBUILD_ONLY="s3" -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)
# 安装
sudo make install

2. 编写上传代码

接下来,我们编写一个简单的C++程序来实现文件上传,并添加进度回调功能。以下是示例代码:

#include <aws/core/Aws.h>
#include <aws/s3/S3Client.h>
#include <aws/s3/model/PutObjectRequest.h>
#include <iostream>
#include <fstream>

// 进度回调函数
class ProgressCallback : public Aws::Utils::RateLimits::RateLimiterInterface {
public:
    // 构造函数,初始化总字节数
    ProgressCallback(uint64_t totalBytes) : totalBytes(totalBytes), uploadedBytes(0) {}

    // 处理上传进度
    uint64_t OnWriteWithLimiter(const uint8_t* buffer, uint64_t bytes) override {
        uploadedBytes += bytes;
        // 计算上传百分比
        double progress = (double)uploadedBytes / totalBytes * 100;
        std::cout << "Upload progress: " << progress << "%" << std::endl;
        return bytes;
    }

private:
    uint64_t totalBytes;
    uint64_t uploadedBytes;
};

int main() {
    // 初始化AWS SDK
    Aws::SDKOptions options;
    Aws::InitAPI(options);

    // 创建S3客户端
    Aws::Client::ClientConfiguration clientConfig;
    Aws::S3::S3Client s3Client(clientConfig);

    // 定义存储桶名称和对象键
    Aws::String bucketName = "your-bucket-name";
    Aws::String objectKey = "your-object-key";
    std::string filePath = "your-file-path";

    // 打开文件
    std::ifstream file(filePath, std::ios::binary);
    if (!file.is_open()) {
        std::cerr << "Failed to open file." << std::endl;
        return 1;
    }

    // 获取文件大小
    file.seekg(0, std::ios::end);
    uint64_t fileSize = file.tellg();
    file.seekg(0, std::ios::beg);

    // 创建PutObject请求
    Aws::S3::Model::PutObjectRequest putObjectRequest;
    putObjectRequest.WithBucket(bucketName).WithKey(objectKey);

    // 创建进度回调对象
    ProgressCallback progressCallback(fileSize);
    putObjectRequest.SetBody(std::make_shared<Aws::FStream>(filePath, std::ios_base::in | std::ios_base::binary));
    putObjectRequest.SetGeneralProgressCallback(
        [&progressCallback](const Aws::Http::HttpRequest &, uint64_t bytesSent, uint64_t totalBytes) {
            progressCallback.OnWriteWithLimiter(nullptr, bytesSent);
        }
    );

    // 发送请求
    auto putObjectOutcome = s3Client.PutObject(putObjectRequest);
    if (putObjectOutcome.IsSuccess()) {
        std::cout << "File uploaded successfully." << std::endl;
    } else {
        std::cerr << "Error uploading file: " << putObjectOutcome.GetError().GetMessage() << std::endl;
    }

    // 关闭文件
    file.close();

    // 关闭AWS SDK
    Aws::ShutdownAPI(options);

    return 0;
}

代码解释

  • ProgressCallback类:继承自Aws::Utils::RateLimits::RateLimiterInterface,用于处理上传进度。在OnWriteWithLimiter方法中,我们更新已上传的字节数,并计算上传百分比,然后输出到控制台。
  • main函数:首先初始化AWS SDK,创建S3客户端。然后打开要上传的文件,获取文件大小。接着创建PutObjectRequest请求,并设置存储桶名称、对象键和文件内容。最后,我们创建ProgressCallback对象,并将其与请求关联起来,通过SetGeneralProgressCallback方法设置进度回调函数。

3. 编译和运行

将上述代码保存为upload_file.cpp,然后使用以下命令进行编译:

g++ upload_file.cpp -o upload_file -laws-cpp-sdk-s3 -laws-cpp-sdk-core

运行编译后的可执行文件:

./upload_file

四、注意事项

  1. 网络稳定性:上传进度的准确性依赖于网络的稳定性。如果网络不稳定,可能会导致上传进度出现波动或者停滞不前。在实际应用中,可以考虑添加网络重试机制,当网络中断时自动重试上传。
  2. 内存管理:在处理大文件上传时,要注意内存的使用情况,避免出现内存溢出的问题。可以采用分块上传的方式,每次只读取一部分文件内容进行上传。
  3. 权限设置:确保嵌入式设备具有访问S3存储桶的权限。可以通过AWS IAM(Identity and Access Management)来管理权限,为设备分配合适的角色和权限。

五、文章总结

通过以上步骤,我们成功实现了C++ S3文件上传进度回调功能,让嵌入式设备能够实时显示文件上传进度。在开发过程中,我们要充分考虑应用场景、技术优缺点和注意事项,以确保系统的稳定性和可靠性。同时,我们也可以根据实际需求对代码进行扩展和优化,比如将进度信息显示在设备的显示屏上,或者将进度信息发送到远程服务器进行监控。