C++ SDK: V1 to V2 migration

更新时间:
复制 MD 格式

OSS C++ SDK V2 is a complete rewrite of V1 (aliyun-oss-cpp-sdk) that unifies configuration, simplifies client initialization and error handling, and streamlines request and response body usage. V2 also adds a paginator, a native asynchronous client, and per-request cancellation.

Major breaking changes

When you migrate to the V2 SDK, be aware of the following major breaking changes:

  1. C++ version requirement: V2 requires C++17 or later (V1 required C++11).

  2. Namespace change: The namespace changes from AlibabaCloud::OSS to alibabacloud::oss2, and the header file paths are updated accordingly.

  3. SDK initialization: You no longer need to call InitializeSdk() or ShutdownSdk().

  4. Client creation: credentials, endpoint, region, and transport layer settings are now defined in the ClientConfiguration object, not passed as constructor parameters.

  5. API call pattern: Convenience overloads are removed. Each operation accepts a typed Request object and returns a corresponding Outcome. Method names now use camelCase instead of PascalCase.

  6. Signature algorithm: V2 uses Signature Version 4 by default. This requires the region parameter to be set.

  7. Error handling: V2 uses an Outcome<Result, Error> interface that is compatible with std::expected. Access results using has_value(), value(), and error().

  8. Request and response bodies: The upload request body type changes from std::shared_ptr<std::iostream> to RequestBody. The response body factory changes from IOStreamFactory to SinkFactory.

  9. Redesigned asynchronous operations: In V1, each operation had separate *Async() and *Callable() methods. V2 replaces these with generic asyncCall() and asyncCallback() templates and adds a native asynchronous client, OSSAsyncClient.

  10. CRC-64 check change: Unlike in V1, a streaming read with getObject() in V2 no longer performs a CRC-64 check by default. To perform a check during a download, use getObjectToFile() or manually implement the check by using SinkFactory with CRC64WriteObserver.

Migration steps

Step 1: Environment preparation

  1. Assess your existing code:

    • Identify where the OSS SDK is used in your code.

    • Document the OSS features and APIs you use.

    • Identify dependencies to prevent unexpected issues during migration.

  2. Update your build environment: The V2 SDK requires C++17 or later. Enable C++17 in your compiler and build configuration, for example, by using the -std=c++17 flag or setting CMAKE_CXX_STANDARD 17 in CMake.

  3. Install the V2 SDK: The V2 SDK uses a new code repository, alibabacloud-oss-cpp-sdk-v2. We recommend installing it alongside the V1 SDK to facilitate a gradual migration.

  4. Create a test environment: Create a separate test environment instead of modifying your production environment directly. Set up test data and test cases to validate the migrated code.

Step 2: Modify header files and namespaces

Feature changes

Version 2 uses a new code repository, with updated header file paths and a namespace change from AlibabaCloud::OSS to alibabacloud::oss2. The header file paths for each module are updated as follows:

Module

V1

V2

core

alibabacloud/oss/OssClient.h

alibabacloud/oss2/OSSClient.h

core (async)

--

alibabacloud/oss2/OSSAsyncClient.h

configuration

alibabacloud/oss/client/ClientConfiguration.h

alibabacloud/oss2/ClientConfiguration.h

credentials

alibabacloud/oss/auth/CredentialsProvider.h

alibabacloud/oss2/credentials/CredentialsProvider.h

model

alibabacloud/oss/model/<Name>.h

alibabacloud/oss2/models/<Category>.h

forward declaration

alibabacloud/oss/OssFwd.h

alibabacloud/oss2/OSSFwd.h

encryption

alibabacloud/oss/encryption/*.h

alibabacloud/oss2/crypto/*.h

Code example

Replace V1 header file includes and namespaces with their V2 equivalents:

// v1
#include <alibabacloud/oss/OssClient.h>
using namespace AlibabaCloud::OSS;

// v2
#include "alibabacloud/oss2/OSSClient.h"
#include "alibabacloud/oss2/credentials/CredentialsProvider.h"
namespace oss = alibabacloud::oss2;
        

Step 3: Modify client code

Feature changes

  1. The V2 SDK removes the InitializeSdk() and ShutdownSdk() calls. Initialization and shutdown are no longer required.

  2. The V2 SDK centralizes all configuration in ClientConfiguration. You no longer pass credentials and endpoints as constructor parameters; instead, you set them as fields in ClientConfiguration. Optional fields use std::optional and fall back to sensible defaults when unset.

  3. The V2 SDK uses the V4 signature algorithm by default. Therefore, you must specify a region when configuring the client.

  4. The V2 SDK supports automatic endpoint resolution based on the selected region. As a result, you can access public endpoints without manually setting them.

Code example

V1 example

#include <alibabacloud/oss/OssClient.h>
using namespace AlibabaCloud::OSS;

int main() {
    // In V1, you must initialize the SDK first.
    InitializeSdk();

    ClientConfiguration conf;
    conf.connectTimeoutMs = 20000;
    conf.requestTimeoutMs = 60000;

    auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();
    // The endpoint and credentials are provided to the constructor.
    OssClient client("oss-cn-hangzhou.aliyuncs.com", credentialsProvider, conf);

    // Use the client for subsequent operations...

    // In V1, you must shut down the SDK before the process exits.
    ShutdownSdk();
    return 0;
}
          

V2 example

#include "alibabacloud/oss2/OSSClient.h"
#include "alibabacloud/oss2/ClientConfiguration.h"
#include "alibabacloud/oss2/credentials/CredentialsProvider.h"

namespace oss = alibabacloud::oss2;

int main() {
    // SDK initialization and destruction are not required.
    // Load the default configuration and set a credentials provider.
    auto conf = oss::ClientConfiguration::loadDefault();
    conf.credentialsProvider = std::make_shared<oss::EnvironmentVariableCredentialsProvider>();

    // V2 uses Signature Version 4 by default, so you must set a region. An endpoint is not required for public network access.
    conf.region = "cn-hangzhou";
    conf.connectTimeout = 20000;       // milliseconds
    conf.readWriteTimeout = 60000;     // milliseconds

    oss::OSSClient client(conf);

    // Use the client to perform subsequent operations...
    return 0;
}
          

The following table compares the configuration parameters.

V1

V2

Description

endpoint (constructor)

ClientConfiguration::endpoint

V2 automatically infers the endpoint from the region.

accessKeyId / accessKeySecret (constructor)

ClientConfiguration::credentialsProvider

V2 centralizes credential handling with a credentials provider.

--

ClientConfiguration::region

New in V2. Required for V4 signature.

connectTimeoutMs

connectTimeout

Same unit (milliseconds).

requestTimeoutMs

readWriteTimeout

Renamed.

verifySSL

insecureSkipVerify

Logic is inverted. In V1, true enables verification; in V2, true skips it.

isCname

useCName

Same functionality.

isPathStyle

usePathStyle

Same functionality.

enableCrc64

disableUploadCRC64Check / disableDownloadCRC64Check

The logic is inverted: V2 enables CRC64 checks by default, providing separate options to disable them for uploads and downloads.

signatureVersion

signatureVersion

V1 defaults to "v1", and V2 defaults to "v4" (a region is required).

retryStrategy

retryer / retryMaxAttempts

V2 includes a built-in retry and backoff mechanism.

enableDateSkewAdjustment

disableClockSkewCorrection

The logic is inverted. V2 enables clock skew correction by default and provides this parameter to disable it.

proxyHost / proxyPort / ...

proxyHost

V2 uses a single URL string for proxy configuration.

httpClient

httpTransport

Renamed and uses a different interface.

To use a fixed AccessKey/SecretKey instead of an environment variable, use StaticCredentialsProvider. In V1, you pass the AccessKey/SecretKey directly to the constructor. In V2, you set it via credentialsProvider:

Initialize the client

This topic describes two ways to initialize the C++ SDK for OSS:

    

        Method 1 (Recommended): Starting with v1.9.0, we recommend configuring a ClientConfiguration object and using it to initialize the client. This method is more flexible, allowing you to configure the connection timeout, CNAME, proxy, and credentials provider. With this method, the SDK automatically identifies the region from the endpoint, so you do not need to specify it. 
        

          Method 2: Pass the endpoint, access key ID, and access key secret directly to the constructor to initialize the client. This method is simple but has limited scalability. For example, this method does not support using temporary credentials to access OSS.
        
        
        
// v1
OssClient client("oss-cn-hangzhou.aliyuncs.com", "ak", "sk", conf);

// v2
auto conf = oss::ClientConfiguration::loadDefault();
conf.region = "cn-hangzhou";
conf.credentialsProvider = std::make_shared<oss::StaticCredentialsProvider>("ak", "sk");
oss::OSSClient client(conf);
        

Step 4: Modify the basic API call

Feature changes

  1. V2 uses a unified calling pattern: method names use camelCase, each operation accepts an <OperationName>Request parameter and returns an <OperationName>Outcome (an Outcome<Result, Error>). Convenience overloads from V1, such as PutObject(bucket, key, content), are removed.

  2. Request objects are built using set* method chaining. All operation methods share the following signature:

    OutcomeType operationName(const models::OperationNameRequest& request, const OperationOptions* options = nullptr)
                

Code example

The following compares the V1 and V2 methods for uploading a file:

// v1
auto outcome = client.PutObject("mybucket", "mykey",
    std::make_shared<std::stringstream>("hello"));
if (outcome.isSuccess()) {
    auto& result = outcome.result();
    // Use the result.
} else {
    auto& err = outcome.error();
    std::cerr << err.Code() << ": " << err.Message() << std::endl;
}

// v2
auto outcome = client.putObject(
    oss::models::PutObjectRequest()
        .setBucket("mybucket")
        .setKey("mykey")
        .setBody(oss::RequestBody::fromString("hello")));
if (outcome.has_value()) {
    auto& result = outcome.value();   // or *outcome
    // Use the result.
} else {
    auto& err = outcome.error();
    std::cerr << err.getCode() << ": " << err.getMessage() << std::endl;
}
        

Most methods were changed from PascalCase to camelCase, while a few were also renamed:

V1

V2

Description

PutObject()

putObject()

Case change only

GetObject()

getObject()

Case change only

HeadObject()

headObject()

Case change only

CreateBucket()

putBucket()

Renamed for API consistency.

DeleteObjects()

deleteMultipleObjects()

Renamed for clarity.

SetObjectAcl()

putObjectAcl()

Renamed for API consistency.

CreateSymlink()

putSymlink()

Renamed for API consistency.

Error handling

V1 uses the Outcome type, accessed via isSuccess(), result(), and error(). V2 uses Outcome<Result, Error>, which is compatible with std::expected. The following table compares the two versions:

Actions

V1

V2

Return type

Outcome

Outcome<Result, Error>

Success check

isSuccess()

has_value()

Result retrieval

result()

value() or operator*

Error retrieval

error()

error()

Error code

error().Code()

error().getCode()

Error message

error().Message()

error().getMessage()

Request ID

error().RequestId()

error().getRequestId()

Error category

--

error().getEC() (std::error_code)

Request URL

--

error().getRequestTarget()

// v1
auto outcome = client.GetObject(request);
if (outcome.isSuccess()) {
    auto& result = outcome.result();
    // Use result.Content().
} else {
    std::cerr << outcome.error().Code() << ": " << outcome.error().Message() << std::endl;
}

// v2
auto outcome = client.getObject(request);
if (!outcome.has_value()) {
    auto& err = outcome.error();
    std::cerr << "Code: " << err.getCode() << std::endl;
    std::cerr << "Message: " << err.getMessage() << std::endl;
    std::cerr << "EC: " << err.getEC() << std::endl;
    std::cerr << "Request ID: " << err.getRequestId() << std::endl;
    std::cerr << "Request Target: " << err.getRequestTarget() << std::endl;
    return 1;
}
auto& result = outcome.value();
// Use result.getBody().
        

When you build with -DUSE_STD_EXPECTED=ON (C++23), Outcome becomes a type alias for std::expected and supports operations such as .and_then(), .transform(), and .or_else().

To support migration from V1, V2 retains the legacy interfaces (isSuccess() / getResult() / getError()). Note: These legacy interfaces are not available in std::expected mode.

Request body

In V1, all operations use std::shared_ptr<std::iostream> as the request body. V2 provides four factory methods in RequestBody, each suited to a different data source:

Factory method

Underlying type

Ownership

Description

RequestBody::fromString(data)

StringContent

Owning (copy/move)

Copies or moves a std::string into the request body. Ideal for most scenarios.

RequestBody::fromFile(path)

FileContent

Owning (file path)

Reads data from a file path and automatically reopens the file on retry.

RequestBody::fromStream(stream)

StreamContent

Shared (shared_ptr)

Wraps a std::shared_ptr<std::istream>. Returns EmptyContent when passed a null pointer.

RequestBody::fromMemory(data, len)

MemoryContent

Non-owning (zero-copy)

Provides a zero-copy reference to an existing memory buffer. The caller must ensure the data remains valid until the request completes.

// v2 -- fromString: Owns a copy of the data
auto outcome = client.putObject(
    oss::models::PutObjectRequest()
        .setBucket("bucket")
        .setKey("key")
        .setBody(oss::RequestBody::fromString("hello world")));

// v2 -- fromFile: Reads from a file, auto-reopening on retry
auto outcome2 = client.putObject(
    oss::models::PutObjectRequest()
        .setBucket("bucket")
        .setKey("key")
        .setBody(oss::RequestBody::fromFile("/path/to/data.bin")));

// v2 -- fromStream: Wraps a shared istream
auto ifs = std::make_shared<std::ifstream>("data.bin", std::ios::binary);
auto outcome3 = client.putObject(
    oss::models::PutObjectRequest()
        .setBucket("bucket")
        .setKey("key")
        .setBody(oss::RequestBody::fromStream(ifs)));

// v2 -- fromMemory: Zero-copy, non-owning reference
const char* buf = getBuffer();
size_t bufLen = getBufferSize();
auto outcome4 = client.putObject(
    oss::models::PutObjectRequest()
        .setBucket("bucket")
        .setKey("key")
        .setBody(oss::RequestBody::fromMemory(buf, bufLen)));
        

V2 also provides the putObjectFromFile() convenience method, which takes a file path directly:

auto outcome = client.putObjectFromFile(
    oss::models::PutObjectRequest()
        .setBucket("bucket")
        .setKey("key"),
    "/path/to/file");
        

Response body

V1 and V2 both return download data as a std::shared_ptr<std::iostream> by default and support a custom response factory, but their interfaces differ:

Actions

V1

V2

Response body retrieval

result.Content()

result.getBody()

Response body type

std::shared_ptr<std::iostream>

std::shared_ptr<std::iostream>

Custom factory

request.setResponseStreamFactory(factory)

request.setSinkFactory(factory)

Factory type

IOStreamFactory = std::function<std::shared_ptr<std::iostream>()>

SinkFactory, containing std::function<std::shared_ptr<ByteWriter>(int64_t size, const HeaderCollection& headers)>

Factory parameters

None

Content length + response headers

Retry support

--

The factory.isOneShot flag

// v1 -- Read from an iostream
auto outcome = client.GetObject(request);
if (outcome.isSuccess()) {
    auto& content = outcome.result().Content();
    std::string data(std::istreambuf_iterator<char>(*content), {});
}

// v1 -- Use a custom response stream factory
request.setResponseStreamFactory([&]() {
    return std::make_shared<std::fstream>("/path/to/file",
        std::ios::out | std::ios::binary);
});

// v2 -- Read from an iostream
auto outcome = client.getObject(request);
if (outcome.has_value()) {
    auto& body = outcome->getBody();
    std::string data(std::istreambuf_iterator<char>(*body), {});
}

// v2 -- Perform a zero-copy download to a buffer using a custom sink
std::vector<uint8_t> buf(4 * 1024 * 1024);
oss::SinkFactory factory;
factory.supplier = [&buf](std::int64_t, const oss::HeaderCollection&)
    -> std::shared_ptr<oss::ByteWriter> {
    return std::make_shared<oss::MemoryWriter>(buf.data(), buf.size());
};
factory.isOneShot = false;  // Enable retries

auto outcome2 = client.getObject(
    oss::models::GetObjectRequest()
        .setBucket("bucket")
        .setKey("key")
        .setSinkFactory(factory));
        

Step 5: Modify advanced API invocation (Optional)

Presigned URL

Changes

V1's GeneratePresignedUrl() returns a URL string. V2's presign() returns a PresignResult containing the URL, HTTP method, expiration, and signed request headers.

Code example
// Method 1: Simple generation
auto outcome = client.GeneratePresignedUrl("bucket", "key", 3600, Http::Method::Get);
if (outcome.isSuccess()) {
    std::cout << outcome.result() << std::endl;
}

// Method 2: Generation with PresignOptions
oss::models::PresignOptions opts;
opts.setExpiresDuration(std::chrono::seconds(3600));

auto outcome = client.presign(
    oss::models::GetObjectRequest()
        .setBucket("bucket")
        .setKey("key"),
    &opts);
if (outcome.has_value()) {
    std::cout << "URL: " << outcome->getUrl() << std::endl;
    std::cout << "Method: " << outcome->getMethod() << std::endl;
}
        

Asynchronous operation

Changes

In V1, OSSClient provides separate *Async() and *Callable() methods for each operation. V2 offers two approaches: OSSClient uses generic template methods asyncCall() / asyncCallback(), which require an executor; OSSAsyncClient uses native asynchronous HTTP transport and does not require an executor.

Item

V1 OssClient

V2 OSSClient

V2 OSSAsyncClient

Header file

alibabacloud/oss/OssClient.h

alibabacloud/oss2/OSSClient.h

alibabacloud/oss2/OSSAsyncClient.h

HTTP transport

Synchronous (thread pool wrapper)

Synchronous (thread pool wrapper)

Native asynchronous HTTP

Executor required

No (built-in)

Yes (conf.executor)

No

Future-based

GetObjectCallable()

asyncCall(request)

asyncCall(request)

Callback-based

GetObjectAsync(req, cb)

asyncCallback(req, cb)

getObjectAsync(req, cb)

Method style

Each operation is independent *Async() / *Callable()

Generic template for all operations

Independent operations *Async() + generic asyncCall()

Code example
// v1 - Each operation has a dedicated future-based method
auto task = client.GetObjectCallable(request);
auto outcome = task.get();

// v2 OSSClient - Requires an executor and uses the generic asyncCall()
conf.executor = std::make_shared<oss::ThreadPoolExecutor>(4);
oss::OSSClient client(conf);
auto future = client.asyncCall(
    oss::models::GetObjectRequest()
        .setBucket("bucket")
        .setKey("key"));
auto outcome = future.get();

// v2 OSSAsyncClient - Native asynchronous, no executor required
oss::OSSAsyncClient asyncClient(conf);
asyncClient.getObjectAsync(
    oss::models::GetObjectRequest()
        .setBucket("bucket")
        .setKey("key"),
    [](oss::GetObjectOutcome outcome) {
        if (outcome.has_value()) { /* ... */ }
    });
        

Pagination

Changes

V1 requires manual pagination with a marker. V2 provides makePaginator() for automatic pagination, supporting ListBucketsRequest, ListObjectsRequest, ListObjectsV2Request, ListObjectVersionsRequest, ListMultipartUploadsRequest, and ListPartsRequest.

Code example
// Method 1: Manual Pagination
std::string marker;
do {
    ListObjectsRequest request("mybucket");
    if (!marker.empty()) request.setMarker(marker);
    auto outcome = client.ListObjects(request);
    if (!outcome.isSuccess()) break;
    for (auto& obj : outcome.result().ObjectSummarys()) {
        // Process obj
    }
    if (outcome.result().IsTruncated()) {
        marker = outcome.result().NextMarker();
    } else {
        break;
    }
} while (true);

// Method 2: Automatic Pagination
auto paginator = oss::makePaginator(client,
    oss::models::ListObjectsRequest()
        .setBucket("mybucket")
        .setMaxKeys(100));
while (paginator.hasNext()) {
    auto outcome = paginator.nextPage();
    if (!outcome.has_value()) break;
    for (auto& obj : outcome->getContents()) {
        // Process obj
    }
}

Cancel request

What's new

Version 1 only supports client-level cancellation. Version 2 adds per-request cancellation using a CancellationToken.

Level

V1

V2

Single request

--

Set CancellationToken using OperationOptions.

Client level

client.DisableRequest() / client.EnableRequest()

client.disableRequest() / client.enableRequest()

Code sample

To cancel a single request, use a CancellationTokenSource to create a token for immediate or deadline-based cancellation:

// v2 -- Cancel a single request from another thread.
auto cts = oss::CancellationTokenSource::create();

oss::OperationOptions opts;
opts.cancellationToken = cts->getToken();

// Start the request in another thread.
auto future = std::async([&]() {
    return client.getObject(request, &opts);
});

// Cancel it from the current thread.
cts->cancel();

// Or set a deadline-based cancellation.
auto cts2 = oss::CancellationTokenSource::create();
cts2->cancelAfter(std::chrono::seconds(30));
opts.cancellationToken = cts2->getToken();

// v2 -- Cancel all requests on the client (including requests that use a CancellationToken).
client.disableRequest();       // Blocks new requests and aborts pending requests.
cts->cancel();                 // Also cancels in-flight requests that hold this token.
// ... Then re-enable.
client.enableRequest();
        

Client-level cancellation disables all requests on that client instance, causing new requests to fail immediately until the client is re-enabled. Note that disableRequest() only blocks new HTTP calls. If you also use a per-request CancellationToken, you must explicitly cancel those tokens to interrupt in-flight requests.

Resumable upload

Changes

V1 offers ResumableUploadObject(), ResumableDownloadObject(), and ResumableCopyObject(). In V2, getObjectToFile() automatically resumes downloads after a network failure using range requests.

Scenario

V1

V2

Upload file

PutObject(bucket, key, filePath)

putObjectFromFile(request, filePath)

Download to file

GetObject(bucket, key, filePath)

getObjectToFile(request, filePath)

Upload large file (resumable transfer)

ResumableUploadObject(request)

Uploader (TBD)

Download large file (resumable transfer)

ResumableDownloadObject(request)

Downloader (TBD). Currently, getObjectToFile() automatically resumes downloads using Range requests.

Convenience method

Changes

V1 offered many convenience overloads such as GetObject(bucket, key) and PutObject(bucket, key, content). V2 replaces them with a single method per operation that accepts a chained request object, and provides equivalent extension methods:

V1

V2

PutObject(bucket, key, filePath)

putObjectFromFile(request, filePath)

GetObject(bucket, key, filePath)

getObjectToFile(request, filePath)

DoesBucketExist(bucket)

isBucketExist(bucket)

DoesObjectExist(bucket, key)

isObjectExist(bucket, key)

Client-side encryption

Changes

V1 uses OssEncryptionClient (inheriting from OssClient) with EncryptionMaterials and CryptoConfiguration. V2 uses OSSEncryptionClient with a simplified MasterCipher interface and EncryptionConfiguration.

Code sample
// v1
#include <alibabacloud/oss/OssEncryptionClient.h>
#include <alibabacloud/oss/encryption/CryptoConfiguration.h>
#include <alibabacloud/oss/encryption/EncryptionMaterials.h>

auto materials = std::make_shared<SimpleRSAEncryptionMaterials>(
    publicKey, privateKey, description);
CryptoConfiguration cryptoConf;
OssEncryptionClient eclient(endpoint, credProvider, conf, materials, cryptoConf);
auto outcome = eclient.PutObject(request);

// v2
#include "alibabacloud/oss2/crypto/OSSEncryptionClient.h"
#include "alibabacloud/oss2/crypto/RsaMasterCipher.h"

auto masterCipher = oss::crypto::makeRsaMasterCipher(publicKeyPem, privateKeyPem, description);
oss::crypto::EncryptionConfiguration encConfig;
encConfig.masterCipher = masterCipher;
oss::OSSEncryptionClient eclient(conf, std::move(encConfig));

auto outcome = eclient.putObject(
    oss::models::PutObjectRequest()
        .setBucket("bucket")
        .setKey("key")
        .setBody(oss::RequestBody::fromString("data")));
        

The V2 OSSEncryptionClient supports putObject, getObject, headObject, getObjectMeta, initiateMultipartUpload, uploadPart, completeMultipartUpload, abortMultipartUpload, and listParts, and allows you to access the underlying OSSClient through unwrap() for non-encrypted operations.

Step 6: Test and validate

  1. Write unit tests: Write a unit test for each migrated feature, covering core functionality (such as upload, download, list, and delete), edge cases (such as extremely large or small payloads and invalid input), and error handling (such as network timeouts). Verify that the behavior matches V1.

  2. Run incremental testing: Adopt an incremental migration approach. Start with a small, self-contained module, write comprehensive tests for it, and once the tests pass, gradually migrate other components. This approach helps avoid large-scale rollbacks.

  3. Run compatibility testing: Verify that the V2 SDK is compatible with your existing internal systems, third-party services, and the operating systems and runtime environments you support.

Other changes

Credentials

V2 provides equivalent credentials providers with adjusted class names. The environment variable names remain the same: OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET, and OSS_SESSION_TOKEN.

V1

V2

SimpleCredentialsProvider

StaticCredentialsProvider

EnvironmentVariableCredentialsProvider

EnvironmentVariableCredentialsProvider

CredentialsProvider (interface)

CredentialsProvider (interface)

--

CredentialsProviderFunc (new)

--

AnonymousCredentialsProvider (new)

For custom credentials, V1 requires a class that inherits from CredentialsProvider. V2 introduces CredentialsProviderFunc, which wraps a lambda or std::function<Credentials()> to integrate a custom credentials source without defining a class.

// v1 -- You must define a class.
class MyCredentialsProvider : public AlibabaCloud::OSS::CredentialsProvider {
  public:
    AlibabaCloud::OSS::Credentials getCredentials() override {
        auto [ak, sk, token] = fetchFromSecretsManager();
        return AlibabaCloud::OSS::Credentials(ak, sk, token);
    }
};
auto provider = std::make_shared<MyCredentialsProvider>();
OssClient client(endpoint, provider, conf);

// v2 -- Use CredentialsProviderFunc and a lambda.
conf.credentialsProvider = std::make_shared<oss::CredentialsProviderFunc>(
    []() -> oss::Credentials {
        auto [ak, sk, token] = fetchFromSecretsManager();
        return oss::Credentials(ak, sk, token);
    });
oss::OSSClient client(conf);
        

Retry

Both V1 and V2 enable automatic retries by default, with a maximum of three attempts. V2 uses FullJitterBackoff to implement exponential backoff with jitter.

Item

V1

V2

Default max attempts

3

3

Backoff strategy

Based on scaleFactor

FullJitterBackoff (exponential backoff with jitter)

Configuration method

ClientConfiguration::retryStrategy

ClientConfiguration::retryer / retryMaxAttempts

Per-request override

--

OperationOptions::retryMaxAttempts

Interface

RetryStrategy (inherits shouldRetry + calcDelayTimeMs)

Retryer

CRC-64

CRC-64 is used to verify data integrity during uploads and downloads. Both V1 and V2 enable CRC-64 checks by default for upload operations. In V1, a getObject() streaming download also enables CRC-64 checks. However, in V2, a getObject() streaming read does not perform a CRC-64 check. To perform a CRC-64 check during a download, use getObjectToFile() or manually implement the check by using SinkFactory with CRC64WriteObserver.

Actions

V1

V2

putObject

CRC-64 enabled

CRC-64 enabled

appendObject

CRC-64 enabled

CRC-64 enabled

uploadPart

CRC-64 enabled

CRC-64 enabled

getObject (streaming)

CRC-64 enabled

CRC-64 not checked

getObjectToFile / ResumableDownloadObject

CRC-64 enabled

CRC-64 enabled

Disable upload CRC check

enableCrc64 = false

disableUploadCRC64Check = true

Disable download CRC check

enableCrc64 = false

disableDownloadCRC64Check = true

To manually verify the CRC-64 for a getObject() streaming download in V2, use SinkFactory with CRC64WriteObserver:

// v2 -- Manually verify CRC-64 for a getObject streaming download.
auto crc = std::make_shared<oss::CRC64WriteObserver>();

oss::SinkFactory factory;
factory.isOneShot = false;
factory.supplier = [&crc](std::int64_t, const oss::HeaderCollection&)
    -> std::shared_ptr<oss::ByteWriter> {
    // Reset the CRC on a retry and recalculate from the beginning.
    crc->reset();
    auto file = std::make_shared<oss::OStreamWriter>(
        std::make_shared<std::ofstream>("local.dat", std::ios::binary | std::ios::trunc));
    return std::make_shared<oss::ObservableWriter>(file, crc);
};

auto outcome = client.getObject(
    oss::models::GetObjectRequest()
        .setBucket("bucket")
        .setKey("key")
        .setSinkFactory(factory));

if (outcome.has_value()) {
    auto serverCrc = outcome->getHashCrc64ecmaAsUint64();
    if (serverCrc != 0 && crc->crc() != serverCrc) {
        // CRC mismatch, data may be corrupted.
    }
}
        

HTTP transport

V1 supports only libcurl. V2 supports both libcurl and WinHTTP (Windows only) and provides both synchronous and asynchronous HTTP transport interfaces.

Item

V1

V2

libcurl (synchronous)

Supported (only option)

USE_CURL_TRANSPORT=ON (default)

WinHTTP (synchronous)

--

USE_WINHTTP_TRANSPORT=ON (Windows only)

libcurl async (curl_multi)

--

Supported (CurlMultiTransport)

WinHTTP async

--

Supported (WinHttpAsyncTransport)

Custom synchronous transport

Implement the HttpClient interface and set conf.httpClient

Implement the HttpTransport interface and set conf.httpTransport

Custom asynchronous transport

--

Implement the AsyncHttpTransport interface and set conf.asyncHttpTransport

CMake option

--

USE_CURL_TRANSPORT / USE_WINHTTP_TRANSPORT

Vcpkg feature

--

curl / winhttp

// v2 -- Use the default (libcurl).
auto conf = oss::ClientConfiguration::loadDefault();
oss::OSSClient client(conf);

// v2 -- Use a custom synchronous transport.
conf.httpTransport = myCustomTransport;
oss::OSSClient client(conf);

// v2 -- Use a custom asynchronous transport (for OSSAsyncClient).
conf.asyncHttpTransport = myCustomAsyncTransport;
oss::OSSAsyncClient asyncClient(conf);
        

Transport layer parameters

In V1, all network parameters are in ClientConfiguration. V2 splits them into two layers: common parameters stay in ClientConfiguration and are automatically passed to the default transport, while transport-specific parameters are set through CurlTransportOptions or WinHttpTransportOptions when creating a custom transport.

Common parameters (ClientConfiguration):

V1 ClientConfiguration

V2 ClientConfiguration

Description

connectTimeoutMs

connectTimeout

Same unit (milliseconds)

requestTimeoutMs

readWriteTimeout

Renamed

verifySSL

insecureSkipVerify

Logic is inverted

proxyScheme + proxyHost + proxyPort

--

Moved to transport-specific options. V2's proxyHost includes the scheme.

enabledRedirect

enabledRedirect

Same

Transport-specific parameters (CurlTransportOptions):

V1 ClientConfiguration

V2 CurlTransportOptions

Description

maxConnections

maxConnections

Defaults to 16 (synchronous) and 100 (asynchronous).

caPath

caPath

CA certificate directory (CURLOPT_CAPATH)

caFile

caFile

CA certificate bundle file (CURLOPT_CAINFO)

networkInterface

networkInterface

Bind to a network interface (CURLOPT_INTERFACE)

proxyPort

proxyPort

Proxy port (CURLOPT_PROXYPORT)

proxyUserName

proxyUserName

Proxy authentication username

proxyPassword

proxyPassword

Proxy authentication password

--

enableVerbose

Enable curl verbose debug output

httpInterceptor

requestInterceptor

V1 uses the HttpInterceptor interface. V2 uses a callback function that lets you set any curl option with curl_easy_setopt.

// v1 -- All parameters are in ClientConfiguration.
ClientConfiguration conf;
conf.maxConnections = 32;
conf.caPath = "/etc/ssl/certs";
conf.caFile = "/etc/ssl/certs/ca-certificates.crt";
conf.networkInterface = "eth0";
conf.proxyHost = "proxy.example.com";
conf.proxyPort = 8080;
conf.proxyUserName = "user";
conf.proxyPassword = "pass";

// v2 -- Create CurlTransportOptions and build the transport layer.
#include "alibabacloud/oss2/transport/curl/CurlTransportFactory.h"

oss::CurlTransportOptions opts;
opts.maxConnections = 32;
opts.caPath = "/etc/ssl/certs";
opts.caFile = "/etc/ssl/certs/ca-certificates.crt";
opts.networkInterface = "eth0";
opts.proxyHost = "http://proxy.example.com";
opts.proxyPort = 8080;
opts.proxyUserName = "user";
opts.proxyPassword = "pass";
opts.enableVerbose = true;

auto conf = oss::ClientConfiguration::loadDefault();
conf.httpTransport = oss::CurlTransportFactory::createHttpTransport(opts);
oss::OSSClient client(conf);

// For OSSAsyncClient
conf.asyncHttpTransport = oss::CurlTransportFactory::createAsyncHttpTransport(opts);
oss::OSSAsyncClient asyncClient(conf);
        

Differences between V1 and V2

Item

V1

V2

C++ version requirement

C++11 or later

Requires C++17 or later

Namespace

AlibabaCloud::OSS

alibabacloud::oss2

SDK initialization

Requires calling InitializeSdk() and ShutdownSdk()

No initialization or shutdown is required

Configuration loading

Accepts credentials, endpoint, and other settings as constructor parameters

Centralizes settings in ClientConfiguration; optional fields use std::optional

Signature algorithm

Defaults to V1 signature. A region is not required.

Defaults to V4 signature. A region is required.

Client creation

OssClient(endpoint, ak, sk, conf)

OSSClient(conf)

API call style

PascalCase method names, with convenience overloads

Methods use camelCase and accept typed Request objects.

Error handling

Outcome, accessed by isSuccess(), result(), and error()

Outcome<Result, Error>, which is compatible with std::expected and accessed by has_value(), value(), and error()

Request body

std::shared_ptr<std::iostream>

RequestBody (fromString / fromFile / fromStream / fromMemory)

Response body factory

IOStreamFactory

SinkFactory

Presigned URL interface

GeneratePresignedUrl() returns a URL

presign() returns the URL, HTTP method, expiration time, and signed request headers

Asynchronous operation

Dedicated *Async() and *Callable() methods for each operation

Provides generic OSSClient methods asyncCall() and asyncCallback(), or a fully asynchronous OSSAsyncClient

Paginator

Manual pagination using a marker

makePaginator() for automatic pagination

Credentials provider

SimpleCredentialsProvider and others; custom providers require class inheritance

StaticCredentialsProvider and others; the new CredentialsProviderFunc can directly wrap a lambda

HTTP transport

libcurl only (synchronous)

libcurl and WinHTTP, supporting both synchronous and asynchronous transport

CRC-64 validation for streaming downloads

Performs a default CRC-64 check on streaming downloads.

Does not automatically validate streaming reads. For validation, use getObjectToFile() or SinkFactory with CRC64WriteObserver.

Common migration issues and solutions

C++ version and namespace

Problem: You encounter compilation errors, such as unresolved symbols for AlibabaCloud::OSS, missing namespaces, or missing header files.

Solution:

  • Ensure that your build environment has C++17 or a later version enabled.

  • Change the namespace from AlibabaCloud::OSS to alibabacloud::oss2 and update the header file paths from alibabacloud/oss/* to alibabacloud/oss2/*.

  • Remove calls to InitializeSdk() and ShutdownSdk().

Body type changes

Problem: Upload operations no longer accept std::shared_ptr<std::iostream>, and you must use a new method to retrieve download results.

Solution:

  • For uploads, choose a method based on the data source: RequestBody::fromString for strings, fromFile for file paths (which automatically reopens on a retry), fromStream to wrap an istream, or fromMemory for a zero-copy reference.

  • For downloads, use result.getBody() to retrieve the response body. If you need a custom receiver, use SinkFactory instead of the V1 setResponseStreamFactory method.

Streaming CRC-64 validation

Problem: The V1 getObject() streaming download performs a CRC-64 validation, but the V2 getObject() streaming read does not.

Solution:

  • To validate data integrity during a download, use getObjectToFile(), which has CRC-64 validation enabled by default.

  • To manually validate a streaming download, use SinkFactory with CRC64WriteObserver to calculate the CRC and compare it with the x-oss-hash-crc64ecma value returned by the server.

Related documents