Version 1.6.1+ | Production Ready | Unreal Engine 5.4+
Upload, download, and manage files in Supabase Storage using Blueprint nodes and C++.
Supabase Storage provides authenticated file storage with bucket-based organization. The UE5 plugin exposes async Blueprint nodes for all storage operations including uploads, downloads, and bucket management.
| Feature | Details |
|---|---|
| Max file size | Configurable (default 50 MB) |
| Chunked upload | Supported (default 1 MB chunks) |
| Auth types | User token or Service key |
| Upload modes | Create, Upsert, Update |
┌──────────────────────────────────────┐
│ Upload File Async │
│ │
│ Bucket: "screenshots" │
│ File Path: "player_001/capture.png" │
│ Local Path: "C:/temp/screenshot.png" │
│ │
├────────────┬────────────┬────────────┤
│ OnSuccess │ OnFailure │ OnProgress │
│ │ │ │
▼ ▼ ▼
[Log OK] [Log Err] [Update UI]
┌──────────────────────────────────────────┐
│ Download File Async │
│ │
│ Bucket: "assets" │
│ File Path: "textures/brick_normal.png" │
│ Local Save: "C:/Game/Textures/brick.png" │
│ │
├────────────┬────────────┬────────────────┤
│ OnSuccess │ OnFailure │ OnProgress │
│ │ │ │
▼ ▼ ▼
[Load Tex] [Log Err] [Show Progress]
Uploads a file using the Subsystem connection.
| Pin | Type | Direction | Description |
|---|---|---|---|
| World Context | Object |
Input | World context object |
| Bucket Name | String |
Input | Target storage bucket |
| File Path | String |
Input | Destination path in the bucket |
| Local File Path | String |
Input | Path to the local file |
| Auth Type | EAuthenticationToken |
Input | User or Service auth (default: User) |
| On Success | Boolean Delegate |
Output | Fires on successful upload |
| On Failure | Error Event |
Output | Fires on error |
| On Progress | Upload Progress |
Output | (BytesUploaded, TotalBytes, Progress%) |
| On Chunk Uploaded | Chunk Upload |
Output | (ChunkIndex, TotalChunks) |
Upload with full control over upload behavior.
| Pin | Type | Direction | Description |
|---|---|---|---|
| World Context | Object |
Input | World context object |
| Bucket Name | String |
Input | Target storage bucket |
| File Path | String |
Input | Destination path in bucket |
| Local File Path | String |
Input | Path to the local file |
| Options | FUploadOptions |
Input | Upload configuration (see below) |
| Auth Type | EAuthenticationToken |
Input | User or Service auth |
| On Success | Boolean Delegate |
Output | Fires on success |
| On Failure | Error Event |
Output | Fires on error |
| On Progress | Upload Progress |
Output | Upload progress delegate |
| On Chunk Uploaded | Chunk Upload |
Output | Chunk progress delegate |
Upload from a byte array (in-memory data).
| Pin | Type | Direction | Description |
|---|---|---|---|
| World Context | Object |
Input | World context object |
| Bucket Name | String |
Input | Target storage bucket |
| File Path | String |
Input | Destination path in bucket |
| File Data | Byte Array |
Input | Raw bytes to upload |
| Content Type | String |
Input | MIME type (e.g. image/png) |
| Auth Type | EAuthenticationToken |
Input | User or Service auth |
Upload with progress tracking enabled by default.
Same pins as UploadFileAsync with OnProgress always active.
Upload using a specific connection instead of the Subsystem.
| Pin | Type | Direction | Description |
|---|---|---|---|
| Connection | Supabase Connection |
Input | Specific connection to use |
| (same other pins as UploadFileAsync) |
FUploadOptions)| Field | Type | Default | Description |
|---|---|---|---|
| Metadata | Map<String, String> |
{} |
File metadata key-value pairs |
| CacheControl | String |
"3600" |
Cache-Control header (seconds) |
| ContentType | String |
(auto-detect) | MIME type override |
| UploadMode | EUploadMode |
Upload |
Upload behavior |
| bReportProgress | Bool |
true |
Enable progress events |
| MaxFileSize | Int64 |
52428800 (50 MB) |
Max allowed file size |
| ChunkSize | Int32 |
1048576 (1 MB) |
Chunk size for large files |
| bUseChunkedUpload | Bool |
true |
Enable chunked upload for large files |
EUploadMode)| Mode | Description |
|---|---|
| Upload | Create new file (fails if exists) |
| Upsert | Create or replace existing file |
| Update | Replace existing file only (fails if not found) |
Downloads a file using the Subsystem connection.
| Pin | Type | Direction | Description |
|---|---|---|---|
| World Context | Object |
Input | World context object |
| Bucket Name | String |
Input | Source storage bucket |
| File Path | String |
Input | File path in the bucket |
| Local Save Path | String |
Input | Where to save the downloaded file |
| On Success | Boolean Delegate |
Output | Fires on successful download |
| On Failure | Error Event |
Output | Fires on error |
Download with custom connection and auth type.
| Pin | Type | Direction | Description |
|---|---|---|---|
| Connection | Supabase Connection |
Input | Specific connection to use |
| Auth Type | EAuthenticationToken |
Input | User or Service auth |
| (same other pins as DownloadFileAsync) |
Download with progress tracking.
| Pin | Type | Direction | Description |
|---|---|---|---|
| bTrackProgress | Bool |
Input | Enable progress tracking |
| On Progress | Download Progress |
Output | (Progress 0.0-1.0, BytesDownloaded) |
Creates a new storage bucket.
| Pin | Type | Direction | Description |
|---|---|---|---|
| World Context | Object |
Input | World context object |
| Connection | Supabase Connection |
Input | Active Supabase connection |
| Bucket Name | String |
Input | Name for the new bucket |
| On Success | Boolean Delegate |
Output | Fires on successful creation |
| On Failure | Error Event |
Output | Fires on error |
Bucket names must be valid (use
IsValidBucketName()to validate before calling).
Checks if a storage bucket exists.
| Pin | Type | Direction | Description |
|---|---|---|---|
| World Context | Object |
Input | World context object |
| Connection | Supabase Connection |
Input | Active Supabase connection |
| Bucket Name | String |
Input | Bucket name to check |
| On Success | Boolean Delegate |
Output | true if bucket exists |
| On Failure | Error Event |
Output | Fires on error |
Checks if a specific file exists within a bucket.
| Pin | Type | Direction | Description |
|---|---|---|---|
| World Context | Object |
Input | World context object |
| Connection | Supabase Connection |
Input | Active Supabase connection |
| Bucket Name | String |
Input | Storage bucket name |
| File Path | String |
Input | Full path to the file |
| On Success | Boolean Delegate |
Output | true if file exists |
| On Failure | Error Event |
Output | Fires on error |
The upload node exposes control methods for managing in-progress uploads:
| Method | Description |
|---|---|
CancelUpload() |
Cancel the current upload |
PauseUpload() |
Pause a chunked upload |
ResumeUpload() |
Resume a paused chunked upload |
GetUploadProgress() |
Get current progress (0.0 - 1.0) |
IsUploadInProgress() |
Check if upload is active |
| Function | Returns | Description |
|---|---|---|
GetContentTypeFromExtension(FilePath) |
String |
Auto-detect MIME type from file extension |
IsValidBucketName(BucketName) |
Bool |
Validate bucket name format |
IsValidFilePath(FilePath) |
Bool |
Validate file path format |
void AMyGameMode::UploadScreenshot(const FString& LocalPath)
{
UAsyncUploadFile* Upload = UAsyncUploadFile::UploadFileWithProgressAsync(
this,
TEXT("screenshots"),
FString::Printf(TEXT("player_%s/%s.png"), *PlayerId, *FGuid::NewGuid().ToString()),
LocalPath
);
Upload->OnSuccess.AddDynamic(this, &AMyGameMode::OnScreenshotUploaded);
Upload->OnFailure.AddDynamic(this, &AMyGameMode::OnUploadFailed);
Upload->OnProgress.AddDynamic(this, &AMyGameMode::OnUploadProgress);
Upload->Activate();
}
void AMyGameMode::OnUploadProgress(int64 BytesUploaded, int64 TotalBytes, float Progress)
{
UE_LOG(LogTemp, Log, TEXT("Upload: %.1f%% (%lld / %lld bytes)"), Progress * 100, BytesUploaded, TotalBytes);
}
void AMyGameMode::UploadSaveGame(const FString& SaveDataJson)
{
// Convert JSON string to bytes
TArray<uint8> FileData;
FTCHARToUTF8 Converter(*SaveDataJson);
FileData.Append((uint8*)Converter.Get(), Converter.Length());
FUploadOptions Options;
Options.UploadMode = EUploadMode::Upsert;
Options.ContentType = TEXT("application/json");
Options.Metadata.Add(TEXT("game_version"), TEXT("1.6.1"));
Options.bUseChunkedUpload = false; // Small file, no chunking needed
UAsyncUploadFile* Upload = UAsyncUploadFile::UploadDataAsync(
this,
TEXT("saves"),
FString::Printf(TEXT("%s/save.json"), *PlayerId),
FileData,
Options.ContentType
);
Upload->OnSuccess.AddDynamic(this, &AMyGameMode::OnSaveUploaded);
Upload->Activate();
}
void AMyGameMode::DownloadTexture(const FString& TexturePath)
{
FString SavePath = FPaths::ProjectSavedDir() / TEXT("Downloads") / FPaths::GetCleanFilename(TexturePath);
UAsyncDownloadFile* Download = UAsyncDownloadFile::DownloadFileWithProgress(
this,
TEXT("assets"),
TexturePath,
SavePath,
true // Track progress
);
Download->OnSuccess.AddDynamic(this, &AMyGameMode::OnTextureDownloaded);
Download->OnFailure.AddDynamic(this, &AMyGameMode::OnDownloadFailed);
Download->OnProgress.AddDynamic(this, &AMyGameMode::OnDownloadProgress);
Download->Activate();
}
void AMyGameMode::SafeUpload(const FString& Bucket, const FString& FilePath, const FString& LocalPath)
{
// First check if bucket exists
UAsyncBucketExist* Check = UAsyncBucketExist::BucketExistAsync(this, Connection, Bucket);
Check->OnSuccess.AddLambda([this, Bucket, FilePath, LocalPath](bool bExists)
{
if (bExists)
{
// Upload the file
UAsyncUploadFile* Upload = UAsyncUploadFile::UploadFileAsync(this, Bucket, FilePath, LocalPath);
Upload->OnSuccess.AddDynamic(this, &AMyGameMode::OnUploadDone);
Upload->Activate();
}
else
{
// Create bucket first, then upload
UAsyncCreateBucket* Create = UAsyncCreateBucket::CreateBucketAsync(this, Connection, Bucket);
Create->OnSuccess.AddLambda([this, Bucket, FilePath, LocalPath](bool bOk)
{
UAsyncUploadFile* Upload = UAsyncUploadFile::UploadFileAsync(this, Bucket, FilePath, LocalPath);
Upload->OnSuccess.AddDynamic(this, &AMyGameMode::OnUploadDone);
Upload->Activate();
});
Create->Activate();
}
});
Check->Activate();
}
1. Capture screenshot with UE5 SceneCaptureComponent2D
2. Save to temporary file
3. UploadFileAsync → bucket: "screenshots"
4. Delete local temp file on success
1. Serialize game state to JSON
2. UploadDataAsync → bucket: "saves", mode: Upsert
3. On next login: DownloadFileAsync to restore
1. User selects image from device
2. Resize/compress if needed
3. UploadFileWithOptionsAsync → bucket: "avatars", mode: Upsert
4. Store public URL in user metadata
1. Check BucketFileExistAsync for the asset
2. If exists: DownloadFileAsync to cache directory
3. Load from cache into UE5 texture/mesh
4. If not exists: Show fallback/default asset
| Error | Cause | Solution |
|---|---|---|
| File not found | Local file doesn't exist | Check FPaths::FileExists() before upload |
| Permission denied | RLS policy blocks access | Check Supabase Storage policies |
| File too large | Exceeds MaxFileSize |
Increase MaxFileSize or enable chunked upload |
| Bucket not found | Target bucket doesn't exist | Call CreateBucketAsync first |
| Invalid path | Path contains illegal characters | Use IsValidFilePath() and SanitizeFilePath() |
| Auth failed | Token expired or invalid | Refresh auth token before upload |
BucketExistAsync / BucketFileExistAsync before operationsIsValidBucketName() and IsValidFilePath() before uploadGetContentTypeFromExtension() or set ContentType manuallyCancelUpload() if the player leaves the gameDocumentation for Supabase UE5 Plugin v1.4.1 by MountainLabs UG