Upload Files
Upload files directly from the device to Cloudflare R2. The SDK handles the presigned URL, upload, and confirmation in one call.
Basic upload
Pass a File object, a bucket name, and a path within that bucket. The SDK handles the presigned URL exchange and upload automatically.
import 'dart:io';
final file = File('/path/to/image.jpg');
final result = await Koolbase.storage.upload(
file,
bucket: 'avatars',
path: 'user_${userId}.jpg',
);
print(result.path); // user_abc123.jpg
print(result.bucket); // avatars
print(result.size); // file size in bytes
print(result.contentType); // image/jpegUpload from bytes
If you have file bytes rather than a file path — for example from an image picker — use uploadBytes():
import 'package:image_picker/image_picker.dart';
final picker = ImagePicker();
final picked = await picker.pickImage(source: ImageSource.gallery);
if (picked != null) {
final bytes = await picked.readAsBytes();
final result = await Koolbase.storage.uploadBytes(
bytes,
bucket: 'avatars',
path: 'user_${userId}.jpg',
contentType: 'image/jpeg',
);
print(result.path); // user_abc123.jpg
}Upload with progress
Track upload progress for large files using the onProgress callback:
await Koolbase.storage.upload(
file,
bucket: 'documents',
path: 'reports/${filename}',
onProgress: (sent, total) {
final percent = (sent / total * 100).toStringAsFixed(1);
setState(() => uploadProgress = sent / total);
print('Upload: $percent%');
},
);Content type detection
Koolbase automatically detects the content type from the file extension. You can override it explicitly using the contentType parameter:
// Auto-detected from .pdf extension
await Koolbase.storage.upload(
file,
bucket: 'documents',
path: 'report.pdf',
);
// Explicitly set content type
await Koolbase.storage.upload(
file,
bucket: 'exports',
path: 'data.json',
contentType: 'application/json',
);Supported file types
allowed_mime_types allowlist configured, the upload is rejected up-front with mime_not_allowed — see Buckets for how to configure that allowlist.Overwriting existing files
Uploads are safe-by-default: if a file already exists at the requested bucket+path, the SDK throws a conflict error rather than silently replacing the data. To explicitly overwrite, pass overwrite: true:
// Default — fails if a file already exists at this path
await Koolbase.storage.upload(
file,
bucket: 'avatars',
path: 'user_${userId}.jpg',
);
// Explicit overwrite — replaces the existing file
await Koolbase.storage.upload(
file,
bucket: 'avatars',
path: 'user_${userId}.jpg',
overwrite: true,
);Safe-by-default uploads
overwrite: true, an upload to a path that already holds a file is rejected with KoolbaseStorageConflictException (Flutter) or KoolbaseStorageConflictError (React Native). Your data is never silently replaced. Catch the conflict and prompt the user, or retry with overwrite: true.There's no built-in versioning. If you need to keep older versions of a file, use unique paths (e.g. include a timestamp or UUID in the filename) rather than relying on overwrite.
Error handling
Catch typed exceptions for the cases you want to handle explicitly, and the base KoolbaseStorageException for everything else:
try {
await Koolbase.storage.upload(
file,
bucket: 'avatars',
path: 'user_${userId}.jpg',
);
} on KoolbaseStorageConflictException catch (e) {
// path_conflict — a file already exists at this path
showError('A file already exists at ${e.path ?? "this path"}. '
'Pass overwrite: true to replace.');
} on KoolbaseStorageQuotaException {
// quota_exceeded — bucket size limit hit
showError('Storage is full. Delete some files first.');
} on KoolbaseStorageFileTooLargeException {
// file_too_large — single-file size limit hit
showError('This file is too large for the bucket.');
} on KoolbaseStorageMimeTypeException {
// mime_not_allowed — content type not in the bucket's allowlist
showError('This file type is not allowed in this bucket.');
} on KoolbaseStorageException catch (e) {
// Catch-all — includes upload_expired, validation_error,
// permission_denied, not_found, and any future codes
if (e.code == 'upload_expired') {
showError('Upload took too long. Please try again.');
} else {
showError('Upload failed: ${e.message}');
}
}The Errors reference lists every storage code the server may return. Branch on the typed class where possible; fall back to e.code for codes without a dedicated subclass (currently upload_expired).