StratusSDK A production-ready .NET SDK for integrating with Zoho Catalyst Stratus object storage. StratusSDK provides a strongly-typed, extensible, and developer-friendly abstraction over the Stratus REST API, designed with clean architecture principles and seamless ASP.NET Core integration. Features Strongly-typed request/response models Extensible operation-based architecture Dependency Injection ready Structured error handling with custom StratusException Built-in ProblemDetails integration for APIs Upload, download, metadata, and bucket management support Presigned URL generation support Async-first with full CancellationToken support HttpClient best practices (no socket exhaustion) Designed For ASP.NET Core applications Cloud-native services Background workers Clean architecture projects Developers who want compile-time safety over raw HTTP calls
$ dotnet add package CSharp.StratusSDKA comprehensive .NET SDK for interacting with Zoho Catalyst Stratus cloud storage. Provides easy-to-use APIs for managing buckets, uploading/downloading objects, and performing advanced storage operations.
✨ Object Management
🔐 Security
📦 Bucket Operations
🚀 Advanced Features
Install the StratusSDK package via NuGet:
dotnet add package StratusSDKOr via Package Manager Console:
Install-Package StratusSDKThe SDK supports two initialization patterns depending on your use case:
Use Case: ASP.NET Core applications, Web APIs, Blazor apps
Benefits:
Setup:
using StratusSDK;
var builder = WebApplication.CreateBuilder(args);
// Add Stratus SDK with DI
builder.Services.AddStratusExtensions(options =>
{
options.BucketName = "your-bucket-name";
options.ProjectID = "your-project-id";
options.Region = ERegion.US;
options.ClientID = builder.Configuration["Stratus:ClientID"];
options.ClientSecret = builder.Configuration["Stratus:ClientSecret"];
options.RefreshToken = builder.Configuration["Stratus:RefreshToken"];
});
// Optional: Add Stratus Exception Handler for automatic error handling
builder.Services.AddExceptionHandler<StratusExceptionHandler>();
builder.Services.AddProblemDetails();
var app = builder.Build();
// Enable exception handling middleware
app.UseExceptionHandler();
app.MapControllers();
app.Run();Usage in Controllers/Services:
using StratusSDK;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class FilesController : ControllerBase
{
private readonly IStratusSDK _sdk;
private readonly ILogger<FilesController> _logger;
// SDK is injected automatically
public FilesController(IStratusSDK sdk, ILogger<FilesController> logger)
{
_sdk = sdk;
_logger = logger;
}
[HttpPost("upload")]
public async Task<IActionResult> Upload(IFormFile file)
{
using var stream = file.OpenReadStream();
// Exceptions are automatically handled by StratusExceptionHandler
await _sdk.UploadStreamAsync(
$"uploads/{file.FileName}",
stream);
return Ok(new { message = "File uploaded successfully" });
}
}Configuration in appsettings.json:
{
"Stratus": {
"BucketName": "your-bucket-name",
"ProjectID": "your-project-id",
"Region": "US",
"ClientID": "your-client-id",
"ClientSecret": "your-client-secret",
"RefreshToken": "your-refresh-token"
}
}Use Case: Console applications, background services, legacy applications
Benefits:
Setup:
using StratusSDK;
var options = new StratusOptions
{
BucketName = "your-bucket-name",
ProjectID = "your-project-id",
Region = ERegion.US,
ClientID = "your-client-id",
ClientSecret = "your-client-secret",
RefreshToken = "your-refresh-token"
};
var sdk = StratusSDKFactory.Create(options);Usage:
using StratusSDK;
class Program
{
static async Task Main(string[] args)
{
var options = new StratusOptions
{
BucketName = Environment.GetEnvironmentVariable("STRATUS_BUCKET"),
ProjectID = Environment.GetEnvironmentVariable("STRATUS_PROJECT_ID"),
Region = ERegion.US,
ClientID = Environment.GetEnvironmentVariable("STRATUS_CLIENT_ID"),
ClientSecret = Environment.GetEnvironmentVariable("STRATUS_CLIENT_SECRET"),
RefreshToken = Environment.GetEnvironmentVariable("STRATUS_REFRESH_TOKEN")
};
var sdk = StratusSDKFactory.Create(options);
try
{
// Upload
await sdk.UploadStringAsync(
"test/file.txt",
"Hello, Stratus!");
Console.WriteLine("Upload successful!");
// Download
var response = await sdk.DownloadObjectAsync(new DownloadObjectRequest
{
ObjectKey = "test/file.txt"
});
Console.WriteLine($"Downloaded {response.Data?.Length ?? 0} bytes");
}
catch (StratusException ex)
{
Console.WriteLine($"Error: {ex.Message}");
Console.WriteLine($"Status: {ex.StatusCode}");
}
}
}The StratusExceptionHandler provides automatic exception handling for ASP.NET Core applications:
Features:
StratusException from any SDK operationRegistration:
// In Program.cs or Startup.cs
builder.Services.AddExceptionHandler<StratusExceptionHandler>();
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseExceptionHandler(); // Enable exception handlingResponse Format:
When a StratusException occurs, the handler returns:
{
"type": "https://api.catalyst.zoho.com/baas/v1/project/123/bucket/object",
"title": "Stratus API Error",
"status": 404,
"detail": "Object not found: documents/report.pdf",
"instance": "/api/files/download",
"errorCode": "OBJECT_NOT_FOUND"
}Example with Exception Handler:
using StratusSDK;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/storage")]
public class StorageController : ControllerBase
{
private readonly IStratusSDK _sdk;
public StorageController(IStratusSDK sdk)
{
_sdk = sdk;
}
[HttpGet("download/{*objectKey}")]
public async Task<IActionResult> Download(string objectKey)
{
// No try-catch needed - StratusExceptionHandler handles it
var response = await _sdk.DownloadObjectAsync(new DownloadObjectRequest
{
ObjectKey = objectKey
});
return File(response.Content, response.ContentType, Path.GetFileName(objectKey));
}
[HttpDelete("{*objectKey}")]
public async Task<IActionResult> Delete(string objectKey)
{
// Exceptions are automatically converted to Problem Details
await _sdk.DeleteObjectAsync(objectKey);
return NoContent();
}
}| Feature | DI Pattern | Manual Pattern |
|---|---|---|
| Use Case | ASP.NET Core, Web APIs | Console apps, Scripts |
| Setup Complexity | Medium | Low |
| Exception Handling | Automatic (with handler) | Manual try-catch |
| Testability | Excellent (interface-based) | Good |
| HttpClient Management | Automatic (pooled) | Manual |
| Logging Integration | Built-in | Manual |
| Configuration | appsettings.json | Environment variables/code |
| Lifetime Management | Automatic (scoped) | Manual |
Recommendation:
Note: This quick start uses the manual initialization pattern. For ASP.NET Core applications, see the Dependency Injection setup above.
Create a StratusOptions instance with your Zoho Catalyst credentials:
using StratusSDK;
var options = new StratusOptions
{
BucketName = "your-bucket-name",
ProjectID = "your-project-id",
Region = ERegion.US,
ClientID = "your-client-id",
ClientSecret = "your-client-secret",
RefreshToken = "your-refresh-token"
};var sdk = StratusSDKFactory.Create(options);// Upload a file from disk
await sdk.UploadFileAsync(
"documents/report.pdf",
"report.pdf",
contentType: EContentType.ApplicationPdf);
// Download a file
var response = await sdk.DownloadObjectAsync(new DownloadObjectRequest
{
ObjectKey = "documents/report.pdf"
});
// List objects
var objects = await sdk.ListAllObjectsAsync(
Prefix: "documents/");| Property | Description | Example |
|---|---|---|
BucketName | Your Stratus bucket name | "my-storage-bucket" |
ProjectID | Catalyst project identifier | "1234567890" |
Region | Data center region | ERegion.US |
ClientID | OAuth client ID | "1000.XXXXX" |
ClientSecret | OAuth client secret | "xxxxxxxxxxxxx" |
RefreshToken | OAuth refresh token | "1000.xxxxx.xxxxx" |
Available regions via ERegion enum:
ERegion.US - United StatesERegion.EU - European UnionERegion.IN - IndiaERegion.AU - AustraliaERegion.JP - JapanERegion.CA - CanadaAll examples below are shown in two patterns: Manual (for console apps) and Dependency Injection (for ASP.NET Core).
Manual (Console App):
using StratusSDK;
var options = new StratusOptions
{
BucketName = "documents",
ProjectID = "1234567890",
Region = ERegion.US,
ClientID = "your-client-id",
ClientSecret = "your-client-secret",
RefreshToken = "your-refresh-token"
};
var sdk = StratusSDKFactory.Create(options);
await sdk.UploadFileAsync(
"documents/report.pdf",
"report.pdf",
contentType: EContentType.ApplicationPdf);Dependency Injection (ASP.NET Core):
using StratusSDK;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class DocumentsController : ControllerBase
{
private readonly IStratusSDK _sdk;
public DocumentsController(IStratusSDK sdk)
{
_sdk = sdk;
}
[HttpPost("upload")]
public async Task<IActionResult> UploadDocument(IFormFile file)
{
using var stream = file.OpenReadStream();
await _sdk.UploadStreamAsync(
$"documents/{file.FileName}",
stream);
return Ok(new { message = "Document uploaded successfully" });
}
}Manual (Console App):
var request = new DownloadObjectRequest
{
ObjectKey = "documents/report.pdf",
VersionId = "01hh9hkfdf07y8pnpbwtkt8cf7"
};
var response = await sdk.DownloadObjectAsync(request);
byte[] fileContent = response.Content;
// Save to file
await File.WriteAllBytesAsync("downloaded-report.pdf", fileContent);Dependency Injection (ASP.NET Core):
[HttpGet("download/{*objectKey}")]
public async Task<IActionResult> Download(string objectKey, [FromQuery] string? versionId = null)
{
var request = new DownloadObjectRequest
{
ObjectKey = objectKey,
VersionId = versionId
};
var response = await _sdk.DownloadObjectAsync(request);
return File(
response.Content,
response.ContentType ?? "application/octet-stream",
Path.GetFileName(objectKey)
);
}Manual (Console App):
await sdk.CopyObjectAsync(
"documents/report.pdf",
"archive/2024/report.pdf");
Console.WriteLine("File copied successfully");Dependency Injection (ASP.NET Core):
[HttpPost("copy")]
public async Task<IActionResult> CopyFile([FromBody] CopyRequest copyRequest)
{
await _sdk.CopyObjectAsync(
copyRequest.Source,
copyRequest.Destination);
return Ok(new { message = "File copied successfully" });
}
public record CopyRequest(string Source, string Destination);Manual (Console App):
// Delete a single object
await sdk.DeleteObjectAsync("temp/file1.txt");
// Delete a single object with version
await sdk.DeleteObjectAsync(
"temp/file1.txt",
versionId: "version123");
// Delete multiple objects
await sdk.DeleteObjectsAsync(
new List<string> { "temp/file1.txt", "temp/file2.txt" });
// Delete multiple objects with detailed control
await sdk.DeleteObjectsAsync(
new List<DeleteObjectRequestData>
{
new() { Key = "temp/file1.txt" },
new() { Key = "temp/file2.txt", VersionId = "version123" }
});
Console.WriteLine("Files deleted successfully");Dependency Injection (ASP.NET Core):
[HttpDelete("{*objectKey}")]
public async Task<IActionResult> Delete(string objectKey)
{
await _sdk.DeleteObjectAsync(objectKey);
return NoContent();
}
[HttpDelete("batch")]
public async Task<IActionResult> DeleteMultiple([FromBody] List<string> objectKeys)
{
await _sdk.DeleteObjectsAsync(objectKeys);
return NoContent();
}Manual (Console App):
string? continuationToken = null;
int totalObjects = 0;
do
{
var response = await sdk.ListAllObjectsAsync(
MaxKeys: 100,
ContinuationToken: continuationToken,
Prefix: "documents/");
foreach (var obj in response.Data)
{
Console.WriteLine($"{obj.ObjectKey} - {obj.Size} bytes");
totalObjects++;
}
continuationToken = response.Data.FirstOrDefault()?.ContinuationToken;
}
while (!string.IsNullOrEmpty(continuationToken));
Console.WriteLine($"Total objects: {totalObjects}");Dependency Injection (ASP.NET Core):
[HttpGet("list")]
public async Task<IActionResult> ListObjects(
[FromQuery] string? prefix = null,
[FromQuery] int maxKeys = 100,
[FromQuery] string? continuationToken = null)
{
var response = await _sdk.ListAllObjectsAsync(
MaxKeys: maxKeys,
ContinuationToken: continuationToken,
Prefix: prefix);
return Ok(new
{
objects = response.Data.Select(obj => new
{
key = obj.ObjectKey,
size = obj.Size,
lastModified = obj.LastModified
}),
continuationToken = response.Data.FirstOrDefault()?.ContinuationToken
});
}Manual (Console App):
var response = await sdk.GetPresignedURLAsync(
EPresignedType.Download,
"documents/report.pdf",
new() { ExpireSeconds = 3600 });
string signature = response.Data.Signature;
int expiresIn = response.Data.ExpriresInSeconds;
Console.WriteLine($"Signature: {signature}");
Console.WriteLine($"Expires in: {expiresIn} seconds");Dependency Injection (ASP.NET Core):
[HttpGet("presigned-url")]
public async Task<IActionResult> GeneratePresignedUrl(
[FromQuery] string objectKey,
[FromQuery] string type = "download",
[FromQuery] int expireSeconds = 3600)
{
var presignedType = type.ToLower() == "upload" ? EPresignedType.Upload : EPresignedType.Download;
var response = await _sdk.GetPresignedURLAsync(
presignedType,
objectKey,
new() { ExpireSeconds = expireSeconds });
return Ok(new
{
signature = response.Data.Signature,
expiresInSeconds = response.Data.ExpriresInSeconds,
activeFrom = response.Data.ActiveFrom
});
}Manual (Console App):
var result = await sdk.ExistsObjectAsync("documents/report.pdf");
if (result.Success)
{
Console.WriteLine("File exists!");
}
else
{
Console.WriteLine("File not found.");
}Dependency Injection (ASP.NET Core):
[HttpHead("{*objectKey}")]
public async Task<IActionResult> CheckExists(string objectKey)
{
var result = await _sdk.ExistsObjectAsync(objectKey);
return result.Success ? Ok() : NotFound();
}Manual (Console App):
// Start extraction
var extractResponse = await sdk.ExtractZipObjectAsync(
"archives/data.zip",
"extracted/data/");
string taskId = extractResponse.Data.TaskId;
Console.WriteLine($"Extraction started. Task ID: {taskId}");
// Poll for status
EZipExtractStatus status;
do
{
await Task.Delay(2000); // Wait 2 seconds
var statusResponse = await sdk.GetExtractionStatusAsync(taskId);
status = statusResponse.Data.Status;
Console.WriteLine($"Status: {status}");
}
while (status == EZipExtractStatus.PENDING);
if (status == EZipExtractStatus.COMPLETED)
{
Console.WriteLine("Extraction completed successfully!");
}
else
{
Console.WriteLine("Extraction failed.");
}Dependency Injection (ASP.NET Core):
[HttpPost("extract-zip")]
public async Task<IActionResult> ExtractZip([FromBody] ExtractRequest request)
{
var response = await _sdk.ExtractZipObjectAsync(
request.ZipPath,
request.Destination);
return Accepted(new
{
taskId = response.Data.TaskId,
statusEndpoint = $"/api/storage/extract-status?objectKey={request.ZipPath}"
});
}
[HttpGet("extract-status")]
public async Task<IActionResult> GetExtractionStatus([FromQuery] string taskId)
{
var response = await _sdk.GetExtractionStatusAsync(taskId);
return Ok(new
{
status = response.Data.Status.ToString(),
isComplete = response.Data.Status == EZipExtractStatus.COMPLETED,
isFailed = response.Data.Status == EZipExtractStatus.FAILED
});
}
public record ExtractRequest(string ZipPath, string Destination);Manual (Console App):
await sdk.RenameObjectAsync(
"documents/old-name.pdf",
"documents/new-name.pdf");
Console.WriteLine("File renamed successfully");Dependency Injection (ASP.NET Core):
[HttpPatch("rename")]
public async Task<IActionResult> RenameFile([FromBody] RenameRequest renameRequest)
{
await _sdk.RenameObjectAsync(
renameRequest.CurrentKey,
renameRequest.NewKey);
return Ok(new { message = "File renamed successfully" });
}
public record RenameRequest(string CurrentKey, string NewKey);This section provides detailed information about all data models, request/response classes, enums, and configuration options used in the SDK.
Configuration class for initializing the Stratus SDK.
| Property | Type | Required | Description | Example |
|---|---|---|---|---|
BucketName | string | Yes | Name of the Stratus bucket | "my-storage-bucket" |
ProjectID | string | Yes | Catalyst project identifier | "1234567890" |
Region | ERegion | Yes | Data center region | ERegion.US |
ClientID | string | Yes (required) | OAuth client ID | "1000.XXXXX" |
ClientSecret | string | Yes (required) | OAuth client secret | "xxxxxxxxxxxxx" |
RefreshToken | string | Yes (required) | OAuth refresh token | "1000.xxxxx.xxxxx" |
OrgId | string? | No | Organization ID (optional) | "60012345678" |
Environment | EStratusEnvironment? | No | Environment setting (optional) | EStratusEnvironment.Production |
BaseUrl | string | Read-only | Auto-generated API URL from Region | "https://api.catalyst.zoho.com" |
Example:
var options = new StratusOptions
{
BucketName = "documents",
ProjectID = "1234567890",
Region = ERegion.US,
ClientID = "1000.ABCDEF",
ClientSecret = "secret123",
RefreshToken = "1000.refresh.token"
};Object identifier for deletion (used with DeleteObjectsAsync).
| Property | Type | Required | Description | Example |
|---|---|---|---|---|
Key | string | Yes | Object key to delete | "temp/file.txt" |
VersionId | string? | No | Specific version to delete | "01hh9hkfdf07y8pnpbwtkt8cf7" |
Request model for downloading objects from storage.
| Property | Type | Required | Description | Example |
|---|---|---|---|---|
ObjectKey | string | Yes | Path/key of the object to download | "documents/report.pdf" |
VersionId | string? | No | Specific version to download | "01hh9hkfdf07y8pnpbwtkt8cf7" |
HeaderOptions | DownloadHeaderOptions? | No | Download configuration options | See DownloadHeaderOptions below |
Configuration options for download operations.
| Property | Type | Default | Description | Example |
|---|---|---|---|---|
Range | int? | null | Byte range to download | 1024 |
RetrieveMeta | bool? | null | Include metadata in response | true |
Optional settings for presigned URL generation.
| Property | Type | Default | Description | Example |
|---|---|---|---|---|
ExpireSeconds | int? | 3600 | URL validity in seconds | 7200 (2 hours) |
ActiveFrom | TimeOnly? | null | URL active from time | new TimeOnly(14, 30) |
VersionId | string? | null | Specific version | "version123" |
Optional settings for upload operations.
| Property | Type | Required | Description | Example |
|---|---|---|---|---|
HeaderOptions | UploadHeaderOptions? | No | Upload configuration options | See UploadHeaderOptions below |
VersionId | string? | No | Version ID for versioned buckets | "01hh9hkfdf07y8pnpbwtkt8cf7" |
Configuration options for upload operations.
| Property | Type | Default | Description | Example |
|---|---|---|---|---|
ContentType | EContentType | TextPlain | MIME type of the object | EContentType.ApplicationPdf |
ContentLength | int? | Auto-calculate | Size in bytes | 1024 |
Compress | bool? | true | Enable compression | true |
Overwrite | bool? | null | Overwrite existing objects (non-versioned buckets only) | true |
ExpiresAfter | float | 0 | Auto-delete after seconds (min: 60) | 86400 (24 hours) |
Metadata | Dictionary<string, string>? | null | Custom metadata (max: 2047 chars total) | { "category": "reports" } |
Request body for updating object metadata.
| Property | Type | Required | Description | Example |
|---|---|---|---|---|
Metadata | Dictionary<string, string> | Yes | Custom metadata key-value pairs | { "Author": "Shahil" } |
Represents a Stratus bucket with metadata.
| Property | Type | Description |
|---|---|---|
Name | string | Bucket name |
ProjectDetails | ProjectDetails | Associated project information |
CreatedBy | ModifiedData | Creator information |
CreatedTime | DateTime | Creation timestamp |
ModifiedBy | ModifiedData | Last modifier information |
ModifiedTime | DateTime | Last modification timestamp |
Meta | BucketMeta | Bucket metadata and settings |
Url | string | Bucket URL |
Represents an object in storage.
| Property | Type | Description |
|---|---|---|
KeyType | EStratusKeyType | Object type (File/Folder) |
Key | string | Object key/path |
VersionId | string? | Version identifier |
Size | long | Size in bytes |
ContentType | string? | MIME content type |
ETag | string? | Entity tag for caching |
LastModified | DateTime | Last modification time |
Response from download operation.
| Property | Type | Description |
|---|---|---|
Success | bool | Whether the download succeeded |
Message | string | Status message |
Data | byte[]? | Downloaded file content |
Response from list objects operation.
| Property | Type | Description |
|---|---|---|
Data | List<GetAllObjectResponseData> | List of objects |
Individual object data in list response.
| Property | Type | Description |
|---|---|---|
ObjectKey | string | Object key/path |
Size | long | Size in bytes |
KeyType | string | Type identifier |
VersionId | string? | Version identifier |
ETag | string? | Entity tag |
LastModified | DateTime | Last modification time |
ContinuationToken | string? | Token for next page |
Response from presigned URL generation.
| Property | Type | Description |
|---|---|---|
Data | PresignedUrlData | Presigned URL data |
Presigned URL details.
| Property | Type | Description |
|---|---|---|
Signature | string | URL signature for authentication |
ExpriresInSeconds | int | Validity duration in seconds |
ActiveFrom | int | Activation timestamp |
Response from ZIP extraction status check.
| Property | Type | Description |
|---|---|---|
Data | GetStatusOfZipExtractData | Extraction status data |
ZIP extraction status details.
| Property | Type | Description |
|---|---|---|
Status | EZipExtractStatus | Extraction status (PENDING/COMPLETED/FAILED) |
TaskId | string | Extraction task identifier |
Response from bucket signature creation.
| Property | Type | Description |
|---|---|---|
Status | string | Response status (e.g., "success") |
Success | bool | Whether the operation succeeded |
Data | CreateBucketSignatureData | Signature data |
Bucket signature details.
| Property | Type | Description |
|---|---|---|
Signature | string | The STS policy signature string for secure bucket access |
ExpiryTime | long | Signature expiry timestamp (Unix epoch in milliseconds) |
Available data center regions.
| Value | Description | Location |
|---|---|---|
US | United States | North America |
EU | European Union | Europe |
IN | India | Asia Pacific |
AU | Australia | Asia Pacific |
JP | Japan | Asia Pacific |
CA | Canada | North America |
Example:
var options = new StratusOptions
{
Region = ERegion.EU // Use European data center
};Supported MIME content types for uploads.
| Value | MIME Type | Use Case |
|---|---|---|
ApplicationJson | application/json | JSON files |
TextPlain | text/plain | Text files |
ApplicationOctetStream | application/octet-stream | Binary files |
ImagePng | image/png | PNG images |
ImageJpeg | image/jpeg | JPEG images |
ImageGif | image/gif | GIF images |
VideoMp4 | video/mp4 | MP4 videos |
AudioMpeg | audio/mpeg | MP3 audio |
ApplicationPdf | application/pdf | PDF documents |
ApplicationZip | application/zip | ZIP archives |
Example:
var options = new UploadHeaderOptions
{
ContentType = EContentType.ImageJpeg
};Type of presigned URL to generate.
| Value | Description | Use Case |
|---|---|---|
Upload | Upload URL | Allow temporary upload access |
Download | Download URL | Share files temporarily |
Example:
var response = await sdk.GetPresignedURLAsync(
EPresignedType.Download,
"shared/document.pdf",
new() { ExpireSeconds = 3600 });Type of storage object.
| Value | Description |
|---|---|
File | Regular file object |
Folder | Directory/folder object |
Bucket caching status.
| Value | Description |
|---|---|
Enabled | Caching is active |
Disabled | Caching is inactive |
InProgress | Caching is being configured |
Status of ZIP extraction operation.
| Value | Description |
|---|---|
PENDING | Extraction queued or in progress |
COMPLETED | Extraction finished successfully |
FAILED | Extraction failed |
Example:
var status = await sdk.GetExtractionStatusAsync(taskId);
switch (status.Data.Status)
{
case EZipExtractStatus.COMPLETED:
Console.WriteLine("Extraction complete!");
break;
case EZipExtractStatus.PENDING:
Console.WriteLine("Still extracting...");
break;
case EZipExtractStatus.FAILED:
Console.WriteLine("Extraction failed!");
break;
}Main SDK interface defining all operations.
Methods:
| Method | Return Type | Description |
|---|---|---|
UploadFileAsync() | Task<UploadObjectResponse> | Upload a file from disk |
UploadStreamAsync() | Task<UploadObjectResponse> | Upload from a stream |
UploadStringAsync() | Task<UploadObjectResponse> | Upload string content |
UploadBytesAsync() | Task<UploadObjectResponse> | Upload raw bytes |
DownloadObjectAsync() | Task<DownloadObjectResponse> | Download objects |
DeleteObjectAsync() | Task<DeleteObjectResponse> | Delete a single object |
DeleteObjectsAsync() | Task<DeleteObjectResponse> | Delete multiple objects |
DeletePathAsync() | Task<DeletePathResponse> | Delete path recursively |
CopyObjectAsync() | Task<CopyObjectResponse> | Copy objects |
RenameObjectAsync() | Task<RenameObjectResponse> | Rename objects |
ExistsObjectAsync() | Task<ExistsObjectResponse> | Check object existence |
ExistsBucketAsync() | Task<ExistsBucketResponse> | Check bucket existence |
GetObjectAsync() | Task<GetObjectResponse> | Get object metadata |
GetObjectVersionsAsync() | Task<GetAllObjectVersionsResponse> | List object versions |
ListAllObjectsAsync() | Task<ListAllObjectsResponse> | List objects |
PutObjectMetadataAsync() | Task<PutObjectMetadataResponse> | Update object metadata |
GetBucketAsync() | Task<GetBucketResponse> | Get bucket information |
ListAllBucketsAsync() | Task<ListBucketResponse> | List all buckets |
CreateBucketSignatureAsync() | Task<CreateBucketSignatureResponse> | Create bucket signature |
GetPresignedURLAsync() | Task<PresignedURLResponse> | Generate presigned URL |
ExtractZipObjectAsync() | Task<ExtractZipObjectResponse> | Extract ZIP file |
GetExtractionStatusAsync() | Task<GetStatusOfZipExtractResponse> | Check extraction status |
Usage:
The interface is implemented by the StratusSDK class and can be injected via DI:
public class MyService
{
private readonly IStratusSDK _sdk;
public MyService(IStratusSDK sdk)
{
_sdk = sdk;
}
public async Task ProcessFile()
{
await _sdk.UploadStringAsync(
"file.txt",
"Hello, Stratus!");
}
}UploadFileAsyncUploads a file from disk to storage.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
objectKey | string | Yes | Destination object key |
filePath | string | Yes | Local file path to upload |
contentType | EContentType | No | MIME content type (default: TextPlain) |
options | UploadObjectRequestOptions? | No | Optional upload settings including header options and version ID |
ct | CancellationToken | No | Cancellation token |
Returns: Task<UploadObjectResponse>
await sdk.UploadFileAsync(
"reports/quarterly.pdf",
"C:/reports/quarterly.pdf",
contentType: EContentType.ApplicationPdf);UploadStreamAsyncUploads a stream to storage.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
objectKey | string | Yes | Destination object key |
contentStream | Stream | Yes | The stream to upload |
options | UploadObjectRequestOptions? | No | Optional upload settings including header options and version ID |
ct | CancellationToken | No | Cancellation token |
Returns: Task<UploadObjectResponse>
using var stream = File.OpenRead("data.bin");
await sdk.UploadStreamAsync(
"data/file.bin",
stream);UploadStringAsyncUploads string content to storage.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
objectKey | string | Yes | Destination object key |
content | string | Yes | The string content to upload |
options | UploadObjectRequestOptions? | No | Optional upload settings including header options and version ID |
ct | CancellationToken | No | Cancellation token |
Returns: Task<UploadObjectResponse>
var json = JsonSerializer.Serialize(new { name = "test" });
await sdk.UploadStringAsync(
"config/settings.json",
json);UploadBytesAsyncUploads raw bytes to storage.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
objectKey | string | Yes | Destination object key |
contentBytes | byte[] | Yes | The byte array to upload |
options | UploadObjectRequestOptions? | No | Optional upload settings including header options and version ID |
ct | CancellationToken | No | Cancellation token |
Returns: Task<UploadObjectResponse>
byte[] imageBytes = await File.ReadAllBytesAsync("photo.png");
await sdk.UploadBytesAsync(
"images/photo.png",
imageBytes);DownloadObjectAsyncDownloads an object from the bucket.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
request | DownloadObjectRequest | Yes | Download request with object key, optional version, and header options |
ct | CancellationToken | No | Cancellation token |
Returns: Task<DownloadObjectResponse>
var response = await sdk.DownloadObjectAsync(new DownloadObjectRequest
{
ObjectKey = "documents/report.pdf",
VersionId = "01hh9hkfdf07y8pnpbwtkt8cf7" // optional
});
await File.WriteAllBytesAsync("report.pdf", response.Data!);CopyObjectAsyncCopies an object from one location to another within the bucket.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
objectKey | string | Yes | Source object key to copy from |
destination | string | Yes | Destination key where the object will be copied to |
ct | CancellationToken | No | Cancellation token |
Returns: Task<CopyObjectResponse>
await sdk.CopyObjectAsync(
"documents/report.pdf",
"archive/2024/report.pdf");RenameObjectAsyncRenames an object in the bucket.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
currentKey | string | Yes | The current object key |
renameTo | string | Yes | The new object key |
ct | CancellationToken | No | Cancellation token |
Returns: Task<RenameObjectResponse>
await sdk.RenameObjectAsync(
"documents/draft.pdf",
"documents/final-report.pdf");DeleteObjectAsyncDeletes a single object from the bucket.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
objectKey | string | Yes | The object key to delete |
versionId | string? | No | Specific version to delete |
ttlInSeconds | int? | No | Delay deletion by seconds (min: 60) |
ct | CancellationToken | No | Cancellation token |
Returns: Task<DeleteObjectResponse>
// Delete a single object
await sdk.DeleteObjectAsync("temp/file1.txt");
// Delete a specific version with delayed deletion
await sdk.DeleteObjectAsync(
"temp/file1.txt",
versionId: "version123",
ttlInSeconds: 300);DeleteObjectsAsyncDeletes multiple objects from the bucket. Supports two overloads.
Overload 1 — with DeleteObjectRequestData:
| Parameter | Type | Required | Description |
|---|---|---|---|
objectKeys | List<DeleteObjectRequestData> | Yes | List of objects to delete (supports version IDs) |
ttlInSeconds | int? | No | Delay deletion by seconds (min: 60) |
ct | CancellationToken | No | Cancellation token |
Overload 2 — with string keys:
| Parameter | Type | Required | Description |
|---|---|---|---|
objectKeys | List<string> | Yes | List of object keys to delete |
ttlInSeconds | int? | No | Delay deletion by seconds (min: 60) |
ct | CancellationToken | No | Cancellation token |
Returns: Task<DeleteObjectResponse>
// Delete multiple objects by key
await sdk.DeleteObjectsAsync(
new List<string> { "temp/file1.txt", "temp/file2.txt" });
// Delete multiple objects with version control
await sdk.DeleteObjectsAsync(
new List<DeleteObjectRequestData>
{
new() { Key = "temp/file1.txt" },
new() { Key = "temp/file2.txt", VersionId = "version123" }
});DeletePathAsyncDeletes all objects matching a specified prefix (path) in the bucket.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
prefix | string | Yes | The prefix path to delete all objects under |
ct | CancellationToken | No | Cancellation token |
Returns: Task<DeletePathResponse>
await sdk.DeletePathAsync("temp/uploads/");ExistsObjectAsyncChecks if a specific object exists in the bucket.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
objectKey | string | Yes | Object key to check |
versionId | string? | No | Specific version to check |
ct | CancellationToken | No | Cancellation token |
Returns: Task<ExistsObjectResponse>
var result = await sdk.ExistsObjectAsync("documents/report.pdf");
// Check a specific version
var versionResult = await sdk.ExistsObjectAsync(
"documents/report.pdf",
versionId: "01hh9hkfdf07y8pnpbwtkt8cf7");GetObjectAsyncRetrieves metadata and information about a specific object.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
objectKey | string | Yes | Object key to retrieve metadata for |
versionId | string? | No | Specific version to get metadata for (pass null for latest) |
ct | CancellationToken | No | Cancellation token |
Returns: Task<GetObjectResponse>
var metadata = await sdk.GetObjectAsync("documents/report.pdf");GetObjectVersionsAsyncRetrieves all versions of a specific object.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
objectKey | string | Yes | Object key to list versions for |
maxVersion | int? | No | Maximum number of versions to return |
continuationToken | string? | No | Pagination token from a previous response |
ct | CancellationToken | No | Cancellation token |
Returns: Task<GetAllObjectVersionsResponse>
var versions = await sdk.GetObjectVersionsAsync(
"documents/report.pdf",
maxVersion: 10);ListAllObjectsAsyncLists all objects in the bucket with optional filtering and pagination.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
MaxKeys | int? | No | Maximum number of results per page |
ContinuationToken | string? | No | Pagination token from a previous response |
Prefix | string? | No | Filter objects by key path prefix |
ct | CancellationToken | No | Cancellation token |
Returns: Task<ListAllObjectsResponse>
// List all objects
var result = await sdk.ListAllObjectsAsync();
// List with filtering and pagination
var filtered = await sdk.ListAllObjectsAsync(
MaxKeys: 100,
Prefix: "documents/");PutObjectMetadataAsyncUpdates custom metadata on an existing object.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
objectKey | string | Yes | Object key to update metadata for |
content | PutObjectMetadataRequestBody | Yes | The metadata key-value pairs to set |
ct | CancellationToken | No | Cancellation token |
Returns: Task<PutObjectMetadataResponse>
await sdk.PutObjectMetadataAsync(
"documents/report.pdf",
new()
{
Metadata = new()
{
{ "Author", "Shahil" },
{ "Description", "Quarterly report" }
}
});GetBucketAsyncRetrieves bucket information and metadata.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
ct | CancellationToken | No | Cancellation token |
Returns: Task<GetBucketResponse>
var bucket = await sdk.GetBucketAsync();ListAllBucketsAsyncLists all buckets available in the project.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
ct | CancellationToken | No | Cancellation token |
Returns: Task<ListBucketResponse>
var buckets = await sdk.ListAllBucketsAsync();ExistsBucketAsyncChecks if the configured bucket exists.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
ct | CancellationToken | No | Cancellation token |
Returns: Task<ExistsBucketResponse>
var result = await sdk.ExistsBucketAsync();CreateBucketSignatureAsyncCreates a signature for the bucket to enable secure access.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
ct | CancellationToken | No | Cancellation token |
Returns: Task<CreateBucketSignatureResponse>
var response = await sdk.CreateBucketSignatureAsync();
string signature = response.Data.Signature;
long expiryTime = response.Data.ExpiryTime;GetPresignedURLAsyncGenerates a presigned URL for uploading or downloading an object without authentication.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
Type | EPresignedType | Yes | The type of presigned URL (Upload or Download) |
objectKey | string | Yes | Object key to generate the URL for |
options | PresignedUrlOptions? | No | Optional settings including expiration, activation time, and version ID |
ct | CancellationToken | No | Cancellation token |
Returns: Task<PresignedURLResponse>
var response = await sdk.GetPresignedURLAsync(
EPresignedType.Download,
"documents/report.pdf",
new() { ExpireSeconds = 3600 });
string signature = response.Data.Signature;ExtractZipObjectAsyncExtracts a zipped object in the bucket to a specified destination.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
objectKey | string | Yes | Object key of the ZIP file to extract |
destination | string | Yes | Destination path for the extracted contents |
ct | CancellationToken | No | Cancellation token |
Returns: Task<ExtractZipObjectResponse>
var response = await sdk.ExtractZipObjectAsync(
"archives/data.zip",
"extracted/data/");
string taskId = response.Data.TaskId;GetExtractionStatusAsyncGets the status of a zip extraction operation.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
taskId | string | Yes | The task ID returned from ExtractZipObjectAsync |
ct | CancellationToken | No | Cancellation token |
Returns: Task<GetStatusOfZipExtractResponse>
var status = await sdk.GetExtractionStatusAsync(taskId);
if (status.Data.Status == EZipExtractStatus.COMPLETED)
Console.WriteLine("Extraction complete!");The SDK uses two exception types:
Thrown when API operations fail:
try
{
await sdk.DownloadObjectAsync(request);
}
catch (StratusException ex)
{
Console.WriteLine($"API Error: {ex.Message}");
Console.WriteLine($"Status Code: {ex.StatusCode}");
Console.WriteLine($"Error Code: {ex.ErrorCode}");
// Handle specific error codes
if (ex.StatusCode == HttpStatusCode.NotFound)
{
Console.WriteLine("Object not found");
}
}Thrown when OAuth authentication fails:
try
{
var sdk = StratusSDKFactory.Create(options);
}
catch (StratusAuthenticationException ex)
{
Console.WriteLine($"Authentication failed: {ex.Message}");
// Check your ClientID, ClientSecret, and RefreshToken
}Operations are categorized based on their testing status:
These operations have been thoroughly tested and are ready for production use:
| Operation | Description |
|---|---|
ListAllBucketsAsync() | List all buckets |
ListAllObjectsAsync() | List all objects |
ExistsBucketAsync() | Check bucket existence |
ExistsObjectAsync() | Check object existence |
DownloadObjectAsync() | Download objects |
GetBucketAsync() | Get bucket information |
RenameObjectAsync() | Rename objects |
CopyObjectAsync() | Copy objects |
GetPresignedURLAsync() | Generate presigned URLs (upload and download) |
PutObjectMetadataAsync() | Update object metadata |
UploadFileAsync() | Upload a file from disk |
UploadStreamAsync() | Upload from a stream |
UploadStringAsync() | Upload string content |
UploadBytesAsync() | Upload raw bytes |
ExtractZipObjectAsync() | Extract ZIP file in cloud |
GetExtractionStatusAsync() | Check extraction status |
DeleteObjectAsync() | Delete a single object |
DeleteObjectsAsync() | Delete multiple objects |
DeletePathAsync() | Delete path recursively |
CreateBucketSignatureAsync() | Create bucket signature |
These operations are functional but have limited testing. Use with caution in production:
| Operation | Description | Notes |
|---|---|---|
GetObjectVersionsAsync() | List object versions | Not fully tested yet |
GetObjectVersionsAsync, downloading/checking a specific versionId) have not been fully tested yet.GetObjectAsync has not been explicitly stability-classified in the test suite.Configure uploads with UploadHeaderOptions:
var options = new UploadHeaderOptions
{
ContentType = EContentType.ApplicationJson,
ContentLength = 1024,
Overwrite = true,
ExpiresAfter = 86400, // 24 hours
Compress = true,
Metadata = new Dictionary<string, string>
{
{ "category", "logs" }
}
};Configure downloads with DownloadHeaderOptions:
var options = new DownloadHeaderOptions
{
Range = "bytes=0-1023", // Download first 1KB
RetrieveMeta = true
};Task.WhenAll() for multiple uploadsIssue: Authentication fails
Issue: Object not found
ExistsObjectAsync()Issue: Upload fails
Issue: Timeout errors
docs/html/index.html for complete API referenceThis project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.
Async suffix consistently (UploadFileAsync, GetPresignedURLAsync, GetExtractionStatusAsync, GetObjectVersionsAsync)DeleteObjectAsync now accepts inline parameters (objectKey, versionId?, ttlInSeconds?) instead of a request objectDeleteObjectsAsync overloads for batch deletion (with List<DeleteObjectRequestData> or List<string>)GetExtractionStatusAsync now accepts taskId instead of objectKeyListAllObjectsAsync parameters are now all optional with defaultsCreateBucketSignatureResponse and CreateBucketSignatureData response models to documentationstring parameters instead of request objectsCopyObject() to CopyObjectAsync() for naming consistencyExistsObjectAsync() now returns ExistsObjectResponse instead of boolGetPresignedURL() now accepts (EPresignedType, string, PresignedUrlOptions?) instead of a PresignedUrlRequestobjectKey as the first parameter with optional UploadObjectRequestOptionsPutObjectMetadataAsync() for updating object metadataNonContentUploadObjectRequest with UploadObjectRequestOptionsPresignedUrlRequest with PresignedUrlOptions (only optional fields)Made with ❤️ for .NET developers