Exporting files to Backblaze B2 in .NET using open-source tools
Export a local file to an existing B2 bucket with the open-source AWS SDK for .NET. This complete console example uses B2’s S3-compatible API and limits each file to 64 MiB.
Introduction to Backblaze B2 cloud storage
B2 supports a native API and an S3-compatible API. Here we use S3 operations, bucket names, and the AWS SDK’s signing and retry implementation.
Setting up a Backblaze B2 account
Create a bucket and a dedicated application key with permission to write to it. Record the bucket’s S3 endpoint and region. Use the application key ID as the access key and the application key as the secret; an account ID is not a substitute for the key ID. Use a standard application key, not the master key, for the S3-compatible API.
Installing required .NET libraries
Install the .NET 10 SDK, then create the project with these pinned packages:
dotnet new console --framework net10.0 --name B2Export
cd B2Export
dotnet add package AWSSDK.Core --version 4.0.102.6
dotnet add package AWSSDK.S3 --version 4.0.103.3
Authenticating with Backblaze B2 API in .NET
Supply these environment variables through your shell or secret manager before running the program:
| Variable | Value |
|---|---|
B2_ENDPOINT | Your regional endpoint, such as https://s3.us-west-004.backblazeb2.com |
B2_REGION | The matching region, such as us-west-004 |
B2_BUCKET | Existing bucket name |
B2_KEY_ID | Application key ID |
B2_APPLICATION_KEY | Application key |
The explicit credentials below keep the SDK from searching an AWS profile or instance metadata. Backblaze documents this AWS SDK configuration.
Uploading files to Backblaze B2 using C#
Replace Program.cs with this complete program. The object key is supplied independently of the
local filename; pass it unescaped so the SDK can encode spaces, Unicode, and reserved characters.
using Amazon.Runtime;
using Amazon.S3;
using Amazon.S3.Model;
public static class B2Exporter
{
public static async Task<int> Main(string[] args)
{
using var deadline = new CancellationTokenSource(TimeSpan.FromMinutes(5));
Console.CancelKeyPress += (_, e) => { e.Cancel = true; deadline.Cancel(); };
try
{
if (args.Length != 2) throw new ArgumentException("Pass a local file and an object key.");
var endpoint = new Uri(Required("B2_ENDPOINT"));
if (endpoint.Scheme != "https" || endpoint.UserInfo.Length != 0 ||
endpoint.AbsolutePath != "/" || endpoint.Query.Length != 0 || endpoint.Fragment.Length != 0)
throw new ArgumentException("Use the HTTPS regional endpoint without a bucket or path.");
var config = new AmazonS3Config
{
ServiceURL = endpoint.AbsoluteUri,
AuthenticationRegion = Required("B2_REGION"),
ForcePathStyle = true,
MaxErrorRetry = 2,
RequestChecksumCalculation = RequestChecksumCalculation.WHEN_REQUIRED,
ResponseChecksumValidation = ResponseChecksumValidation.WHEN_REQUIRED
};
using var client = new AmazonS3Client(
Required("B2_KEY_ID"), Required("B2_APPLICATION_KEY"), config);
await UploadAsync(client, Required("B2_BUCKET"), args[0], args[1], deadline.Token);
Console.WriteLine("Uploaded.");
return 0;
}
catch (OperationCanceledException) { Console.Error.WriteLine("Upload canceled or timed out."); }
catch (AmazonS3Exception error) { Console.Error.WriteLine($"Storage returned HTTP {(int)error.StatusCode}."); }
catch (Exception) { Console.Error.WriteLine("Upload failed. Check the file and storage configuration."); }
return 1;
}
private static string Required(string name) =>
Environment.GetEnvironmentVariable(name) is { Length: > 0 } value
? value : throw new ArgumentException($"Missing {name}.");
public static async Task UploadAsync(IAmazonS3 client, string bucket, string path,
string key, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(key)) throw new ArgumentException("An object key is required.");
await using var input = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
if (input.Length > 64L * 1024 * 1024) throw new IOException("This example accepts at most 64 MiB.");
await client.PutObjectAsync(new PutObjectRequest
{
BucketName = bucket,
Key = key,
InputStream = input,
AutoCloseStream = false,
ContentType = "application/octet-stream",
UseChunkEncoding = false
}, cancellationToken);
}
}
Integrating Backblaze uploads into your application
Create a small input and run the program from the project directory:
printf 'example export\n' > report.txt
dotnet run -- report.txt 'reports/report #1.txt'
UploadAsync accepts an IAmazonS3 instance and cancellation token, so an application can reuse
its client and propagate request cancellation. The caller owns the client; the method closes its
file stream on success and failure. Keep the source file unchanged until the upload finishes.
Troubleshooting and best practices
The SDK handles retryable storage failures with at most two retries. Authentication and permission failures need configuration changes. A canceled or failed request can have an uncertain remote outcome, so check the object before rerunning if creating another B2 file version matters.
This example sends one streamed PUT, including for empty files. For larger exports, use the SDK’s multipart facilities and plan for abandoned multipart-upload cleanup. Only optional SDK checksums are disabled for interoperability; HTTPS and request signing remain enabled.
Conclusion
The AWS SDK provides a complete supported signing and upload path without a separate B2 dependency injection adapter. To combine exports with processing, see Transloadit’s File Exporting service.
