CSharp.StratusSDK
1.0.2
See the version list below for details.
dotnet add package CSharp.StratusSDK --version 1.0.2
NuGet\Install-Package CSharp.StratusSDK -Version 1.0.2
<PackageReference Include="CSharp.StratusSDK" Version="1.0.2" />
<PackageVersion Include="CSharp.StratusSDK" Version="1.0.2" />
<PackageReference Include="CSharp.StratusSDK" />
paket add CSharp.StratusSDK --version 1.0.2
#r "nuget: CSharp.StratusSDK, 1.0.2"
#:package CSharp.StratusSDK@1.0.2
#addin nuget:?package=CSharp.StratusSDK&version=1.0.2
#tool nuget:?package=CSharp.StratusSDK&version=1.0.2
Stratus SDK for .NET
A 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.
Table of Contents
- Features
- Installation
- SDK Initialization
- Quick Start
- Configuration
- Usage Examples
- Data Models Reference
- API Reference
- Error Handling
- Operation Stability
- Known Limitations & Improvements
- Best Practices
- Advanced Configuration
- Performance Tips
- Troubleshooting
- Requirements
- Support
- License
- Contributing
- Changelog
Features
✨ Object Management
- Upload and download files with metadata
- Copy, rename, and delete objects
- Version control support
- Automatic compression
🔐 Security
- OAuth 2.0 authentication
- Presigned URLs for temporary access
- Secure credential management
📦 Bucket Operations
- List and manage buckets
- Bucket metadata and configuration
- Multi-region support
🚀 Advanced Features
- ZIP file extraction in cloud
- Pagination support for large listings
- Custom metadata for objects
- Automatic retry and error handling
Installation
Install the StratusSDK package via NuGet:
dotnet add package StratusSDK
Or via Package Manager Console:
Install-Package StratusSDK
SDK Initialization
The SDK supports two initialization patterns depending on your use case:
Option 1: Dependency Injection (Recommended for ASP.NET Core)
Use Case: ASP.NET Core applications, Web APIs, Blazor apps
Benefits:
- Automatic lifetime management
- Built-in exception handling middleware
- Better testability with interfaces
- Integration with ASP.NET Core logging
- Proper HttpClient management
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"
}
}
Option 2: Manual Initialization (For Console Apps & Non-DI Scenarios)
Use Case: Console applications, background services, legacy applications
Benefits:
- Simple and straightforward
- No DI container required
- Full control over SDK lifecycle
- Suitable for scripts and utilities
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}");
}
}
}
Exception Handler (DI Only)
The StratusExceptionHandler provides automatic exception handling for ASP.NET Core applications:
Features:
- Automatically catches
StratusExceptionfrom any SDK operation - Converts exceptions to standardized Problem Details (RFC 7807)
- Logs errors with structured data
- Returns proper HTTP status codes
- Includes error codes and details in response
Registration:
// In Program.cs or Startup.cs
builder.Services.AddExceptionHandler<StratusExceptionHandler>();
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseExceptionHandler(); // Enable exception handling
Response 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();
}
}
Choosing Between Patterns
| 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:
- ✅ Use DI Pattern for web applications, APIs, and long-running services
- ✅ Use Manual Pattern for console apps, scripts, and simple utilities
Quick Start
Note: This quick start uses the manual initialization pattern. For ASP.NET Core applications, see the Dependency Injection setup above.
1. Configure the SDK
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"
};
2. Initialize the SDK
var sdk = StratusSDKFactory.Create(options);
3. Start Using
// 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/");
Configuration
Required Settings
| 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" |
Regions
Available regions via ERegion enum:
ERegion.US- United StatesERegion.EU- European UnionERegion.IN- IndiaERegion.AU- AustraliaERegion.JP- JapanERegion.CA- Canada
Usage Examples
All examples below are shown in two patterns: Manual (for console apps) and Dependency Injection (for ASP.NET Core).
Upload with Metadata
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" });
}
}
Download Specific Version
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)
);
}
Copy Objects
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);
Delete Objects
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();
}
List Objects with Pagination
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
});
}
Generate Presigned URLs
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
});
}
Check Object Existence
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();
}
Extract ZIP Files
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);
Rename Objects
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);
Data Models Reference
This section provides detailed information about all data models, request/response classes, enums, and configuration options used in the SDK.
Configuration
StratusOptions
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"
};
Request Models
DeleteObjectRequestData
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" |
DownloadObjectRequest
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 |
DownloadHeaderOptions
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 |
PresignedUrlOptions
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" |
UploadObjectRequestOptions
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" |
UploadHeaderOptions
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" } |
PutObjectMetadataRequestBody
Request body for updating object metadata.
| Property | Type | Required | Description | Example |
|---|---|---|---|---|
Metadata |
Dictionary<string, string> |
Yes | Custom metadata key-value pairs | { "Author": "Shahil" } |
Response Models
Bucket
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 |
BucketObject
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 |
DownloadObjectResponse
Response from download operation.
| Property | Type | Description |
|---|---|---|
Success |
bool |
Whether the download succeeded |
Message |
string |
Status message |
Data |
byte[]? |
Downloaded file content |
GetAllObjectsResponse
Response from list objects operation.
| Property | Type | Description |
|---|---|---|
Data |
List<GetAllObjectResponseData> |
List of objects |
GetAllObjectResponseData
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 |
PresignedURLResponse
Response from presigned URL generation.
| Property | Type | Description |
|---|---|---|
Data |
PresignedUrlData |
Presigned URL data |
PresignedUrlData
Presigned URL details.
| Property | Type | Description |
|---|---|---|
Signature |
string |
URL signature for authentication |
ExpriresInSeconds |
int |
Validity duration in seconds |
ActiveFrom |
int |
Activation timestamp |
GetStatusOfZipExtractResponse
Response from ZIP extraction status check.
| Property | Type | Description |
|---|---|---|
Data |
GetStatusOfZipExtractData |
Extraction status data |
GetStatusOfZipExtractData
ZIP extraction status details.
| Property | Type | Description |
|---|---|---|
Status |
EZipExtractStatus |
Extraction status (PENDING/COMPLETED/FAILED) |
TaskId |
string |
Extraction task identifier |
CreateBucketSignatureResponse
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 |
CreateBucketSignatureData
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) |
Enumerations
ERegion
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
};
EContentType
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
};
EPresignedType
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 });
EStratusKeyType
Type of storage object.
| Value | Description |
|---|---|
File |
Regular file object |
Folder |
Directory/folder object |
ECachingStatus
Bucket caching status.
| Value | Description |
|---|---|
Enabled |
Caching is active |
Disabled |
Caching is inactive |
InProgress |
Caching is being configured |
EZipExtractStatus
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;
}
Interfaces
IStratusSDK
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!");
}
}
API Reference
Object Operations
UploadFileAsync
Uploads 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);
UploadStreamAsync
Uploads 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);
UploadStringAsync
Uploads 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);
UploadBytesAsync
Uploads 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);
DownloadObjectAsync
Downloads 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!);
CopyObjectAsync
Copies 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");
RenameObjectAsync
Renames 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");
DeleteObjectAsync
Deletes 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);
DeleteObjectsAsync
Deletes 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" }
});
DeletePathAsync
Deletes 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/");
ExistsObjectAsync
Checks 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");
Metadata & Information
GetObjectAsync
Retrieves 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");
GetObjectVersionsAsync
Retrieves 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);
ListAllObjectsAsync
Lists 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/");
PutObjectMetadataAsync
Updates 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" }
}
});
Bucket Operations
GetBucketAsync
Retrieves bucket information and metadata.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
ct |
CancellationToken |
No | Cancellation token |
Returns: Task<GetBucketResponse>
var bucket = await sdk.GetBucketAsync();
ListAllBucketsAsync
Lists all buckets available in the project.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
ct |
CancellationToken |
No | Cancellation token |
Returns: Task<ListBucketResponse>
var buckets = await sdk.ListAllBucketsAsync();
ExistsBucketAsync
Checks if the configured bucket exists.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
ct |
CancellationToken |
No | Cancellation token |
Returns: Task<ExistsBucketResponse>
var result = await sdk.ExistsBucketAsync();
CreateBucketSignatureAsync
Creates 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;
Advanced Operations
GetPresignedURLAsync
Generates 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;
ExtractZipObjectAsync
Extracts 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;
GetExtractionStatusAsync
Gets 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!");
Error Handling
The SDK uses two exception types:
StratusException
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");
}
}
StratusAuthenticationException
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
}
Operation Stability
Operations are categorized based on their testing status:
Stable Operations
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 |
Experimental Operations
These operations are functional but have limited testing. Use with caution in production:
| Operation | Description | Notes |
|---|---|---|
GetObjectVersionsAsync() |
List object versions | Not fully tested yet |
Known Limitations & Improvements
- Version-based operations (
GetObjectVersionsAsync, downloading/checking a specificversionId) have not been fully tested yet. GetObjectAsynchas not been explicitly stability-classified in the test suite.
Best Practices
✅ Do
- Reuse SDK instances - Create once, use multiple times
- Use async/await - All operations are asynchronous
- Handle exceptions - Always wrap calls in try-catch blocks
- Set metadata - Use custom metadata for better organization
- Use pagination - For large listings, use continuation tokens
- Validate inputs - Check object keys and paths before operations
❌ Don't
- Don't hardcode credentials - Use environment variables or secure storage
- Don't ignore errors - Always handle exceptions appropriately
- Don't upload large files synchronously - Use proper async patterns
- Don't forget versioning - Specify version IDs when needed
- Don't skip metadata - Metadata helps with organization and search
Advanced Configuration
Upload Options
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" }
}
};
Download Options
Configure downloads with DownloadHeaderOptions:
var options = new DownloadHeaderOptions
{
Range = "bytes=0-1023", // Download first 1KB
RetrieveMeta = true
};
Performance Tips
- Batch Operations - Use batch delete for multiple objects
- Connection Pooling - Reuse HttpClient (handled automatically)
- Parallel Uploads - Use
Task.WhenAll()for multiple uploads - Pagination - Use appropriate page sizes (100-1000 items)
- Compression - Enable compression for text files
Troubleshooting
Common Issues
Issue: Authentication fails
- Verify ClientID, ClientSecret, and RefreshToken
- Ensure tokens haven't expired
- Check region setting matches your Catalyst account
Issue: Object not found
- Check object key spelling and case sensitivity
- Verify bucket name is correct
- Ensure object exists using
ExistsObjectAsync()
Issue: Upload fails
- Check file size limits
- Verify bucket permissions
- Ensure network connectivity
Issue: Timeout errors
- Increase timeout for large files
- Check network stability
- Verify service availability
Requirements
- .NET 10.0 or higher
- Valid Zoho Catalyst account
- Active Catalyst project with Stratus enabled
Support
- GitHub Issues: Report bugs or request features
- Repository: View source code
- Documentation: See
docs/html/index.htmlfor complete API reference
License
This project is licensed under the MIT License - see the LICENSE file for details.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Changelog
Version 1.2.0
- All public methods now use the
Asyncsuffix consistently (UploadFileAsync,GetPresignedURLAsync,GetExtractionStatusAsync,GetObjectVersionsAsync) DeleteObjectAsyncnow accepts inline parameters (objectKey,versionId?,ttlInSeconds?) instead of a request object- Added
DeleteObjectsAsyncoverloads for batch deletion (withList<DeleteObjectRequestData>orList<string>) GetExtractionStatusAsyncnow acceptstaskIdinstead ofobjectKeyListAllObjectsAsyncparameters are now all optional with defaults- Added
CreateBucketSignatureResponseandCreateBucketSignatureDataresponse models to documentation - Promoted upload, delete, extract, and bucket signature operations from Experimental to Stable
Version 1.1.0
- Simplified public API — most methods now accept inline
stringparameters instead of request objects - Renamed
CopyObject()toCopyObjectAsync()for naming consistency ExistsObjectAsync()now returnsExistsObjectResponseinstead ofboolGetPresignedURL()now accepts(EPresignedType, string, PresignedUrlOptions?)instead of aPresignedUrlRequest- Upload methods now take
objectKeyas the first parameter with optionalUploadObjectRequestOptions - Added
PutObjectMetadataAsync()for updating object metadata - Replaced
NonContentUploadObjectRequestwithUploadObjectRequestOptions - Replaced
PresignedUrlRequestwithPresignedUrlOptions(only optional fields) - Added operation stability classification (Stable / Experimental)
Version 1.0.0
- Initial release
- Core object operations (upload, download, delete, copy, rename)
- Bucket management
- Presigned URL generation
- ZIP extraction support
- Comprehensive error handling
- Full API documentation
Made with ❤️ for .NET developers
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Corvus.UriTemplates (>= 2.3.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.