Use case: Create and upload files to a dataset
You can use the Unity Cloud Assets package to perform the following:
- View the files of an asset.
- Upload files to an asset's dataset.
- Reference files between datasets.
Note
To create and upload assets, as well as add or remove file references, you need an Asset Manager Admin
role at Organization level or an Asset Management Contributor
add-on role at Project level. Asset Management Contributors can manage assets only for the specific projects to which they have access.
Before you start
Before you start, do the following:
- Verify your permissions as described in Asset Manager user roles.
Note
Asset Manager roles specify permissions you have for a single Asset Manager project. Depending on your work, permissions can vary across different projects.
Set up a Unity scene in the Unity Editor with an Organization and Project browser. See the Get started with Asset SDK page for more information.
Create assets in the cloud any of the following ways:
How do I...?
List an asset's datasets and files
List datasets as follows:
- Open the
AssetManagementBehaviour
script you created as described in Get started with Asset SDK. - Add the following code to the end of the class:
public List<IDataset> Datasets { get; private set; }
public async Task GetDatasets()
{
Datasets = null;
if (CurrentAsset == null) return;
var datasets = new List<IDataset>();
var asyncList = CurrentAsset.ListDatasetsAsync(Range.All, CancellationToken.None);
await foreach (var dataset in asyncList)
{
datasets.Add(dataset);
}
Datasets = datasets;
}
List files as follows:
- Open the
AssetManagementBehaviour
script you created as described in Get started with Asset SDK. - Add the following code to the end of the class:
public Dictionary<DatasetId, IEnumerable<IFile>> DatasetFiles { get; } = new();
public async Task GetFilesAsync(DatasetId datasetId)
{
DatasetFiles.Remove(datasetId);
var dataset = Datasets?.FirstOrDefault(d => d.Descriptor.DatasetId == datasetId);
if (dataset == null) return;
DatasetFiles[datasetId] = null;
var files = new List<IFile>();
var fileList = dataset.ListFilesAsync(Range.All, CancellationToken.None);
await foreach (var file in fileList)
{
files.Add(file);
}
files.Sort((a, b) => string.Compare(a.Descriptor.Path, b.Descriptor.Path, StringComparison.Ordinal));
DatasetFiles[datasetId] = files;
}
Upload a file
Upload a file to an asset's dataset as follows:
- Open the
AssetManagementBehaviour
script you created as described in Get started with Asset SDK. - Add the following code to the end of the class:
class LogProgress : IProgress<HttpProgress>
{
string m_Name;
public LogProgress(string name)
{
m_Name = name;
}
public void Report(HttpProgress value)
{
if (!value.UploadProgress.HasValue) return;
Debug.Log($"Upload progress for {m_Name}: {value.UploadProgress * 100} %");
}
}
static string GetRelativePath(string folderPath, string filePath)
{
return string.IsNullOrEmpty(folderPath) ? filePath : Path.GetRelativePath(folderPath, filePath);
}
public async Task UploadFile(IDataset dataset, string filePath, string folderPath = "", bool refreshFiles = true)
{
var fileCreation = new FileCreation(GetRelativePath(folderPath, filePath))
{
Description = "Documentation example file creation.",
};
try
{
var progress = new LogProgress(filePath);
var fileStream = File.OpenRead(filePath);
var file = await dataset.UploadFileAsync(fileCreation, fileStream, progress, default);
await dataset.RefreshAsync(default);
if (refreshFiles)
{
_ = GetFilesAsync(dataset.Descriptor.DatasetId);
}
Debug.Log($"File upload: {file.Descriptor.Path} added and uploaded.");
}
catch (Exception e)
{
Debug.LogError($"Failed to upload file: {fileCreation.Path}. {e}");
}
}
public async Task ReplaceFile(IDataset dataset, string filePath, string folderPath = "", bool refreshFiles = true)
{
var path = GetRelativePath(folderPath, filePath);
if (DatasetFiles.TryGetValue(dataset.Descriptor.DatasetId, out var files))
{
var file = files.FirstOrDefault(f => f.Descriptor.Path == path);
if (file != null)
{
try
{
await dataset.RemoveFileAsync(file.Descriptor.Path, CancellationToken.None);
}
catch (Exception e)
{
Debug.LogError($"Failed to remove file for replace: {file.Descriptor.Path}. {e}");
return;
}
}
}
await UploadFile(dataset, filePath, folderPath, refreshFiles);
}
public async Task ReuploadFile(IDataset dataset, string filePath, string folderPath = "", bool refreshFiles = true)
{
var path = GetRelativePath(folderPath, filePath);
if (DatasetFiles.TryGetValue(dataset.Descriptor.DatasetId, out var files))
{
var file = files.FirstOrDefault(f => f.Descriptor.Path == path);
if (file != null)
{
try
{
var logProgress = new LogProgress(path);
var fileStream = File.OpenRead(filePath);
await file.UploadAsync(fileStream, logProgress, default);
await dataset.RefreshAsync(default);
if (refreshFiles)
{
_ = GetFilesAsync(dataset.Descriptor.DatasetId);
}
Debug.Log($"Re-uplaoded file: {file.Descriptor.Path}.");
}
catch (Exception e)
{
Debug.LogError($"Failed to re-upload file: {file.Descriptor.Path}. {e}");
}
return;
}
}
await UploadFile(dataset, filePath, folderPath, refreshFiles);
}
The code snippet does the following:
- Provides a method to upload a new file to a dataset.
- Provides a method to replace the content of an existing file in a dataset.
- Provides a method to replace a file in a dataset with another file.
Upload a folder
Upload a folder to an asset's dataset as follows:
- Open the
AssetManagementBehaviour
script you created as described in Get started with Asset SDK. - Add the following code to the end of the class:
public async Task UploadFolderAsync(IDataset dataset, string folderPath, string uploadType)
{
var parentDirectoryPath = Directory.GetParent(folderPath)?.FullName;
var files = Directory.GetFiles(folderPath, "*.*", SearchOption.AllDirectories);
var tasks = new List<Task>();
foreach (var file in files)
{
switch (uploadType)
{
case "Replace":
tasks.Add(ReplaceFile(dataset, file, parentDirectoryPath, false));
break;
case "Reupload":
tasks.Add(ReuploadFile(dataset, file, parentDirectoryPath, false));
break;
default:
tasks.Add(UploadFile(dataset, file, parentDirectoryPath, false));
break;
}
}
try
{
await Task.WhenAll(tasks);
_ = GetFilesAsync(dataset.Descriptor.DatasetId);
Debug.Log($"Folder: {folderPath} uploaded.");
}
catch (Exception e)
{
Debug.LogError($"Failed to upload folder: {folderPath}. {e}");
}
}
The code snippet provides a method to upload the entire content of a folder to a dataset.
Note
The method checks for existing files in the dataset and replaces their content. If an existing file is not found, it uploads the file as new.
Reference a file in a different dataset
Reference a file in a different dataset as follows:
- Open the
AssetManagementBehaviour
script you created as described in Get started with Asset SDK. - Add the following code to the end of the class:
public async Task LinkFile(IDataset dataset, IFile file)
{
try
{
await dataset.AddExistingFileAsync(file.Descriptor.Path, file.Descriptor.DatasetId, CancellationToken.None);
Debug.Log($"File: {file.Descriptor.Path} linked to dataset {dataset.Descriptor.DatasetId}.");
// If the dataset files are already loaded, refresh them
if (DatasetFiles.ContainsKey(dataset.Descriptor.DatasetId))
{
_ = GetFilesAsync(dataset.Descriptor.DatasetId);
}
}
catch (Exception e)
{
Debug.LogError($"Failed to link file: {file.Descriptor.Path}. {e}");
}
}
The code snippet does the following:
- Links a file to a dataset.
- Prints a success message to the console on success or an error message if the linking fails.
Remove a file reference from a dataset
Remove a file reference from a dataset as follows:
- Open the
AssetManagementBehaviour
script you created as described in Get started with Asset SDK. - Add the following code to the end of the class:
public async Task UnlinkFile(IDataset dataset, IFile file)
{
try
{
await dataset.RemoveFileAsync(file.Descriptor.Path, CancellationToken.None);
Debug.Log($"File: {file.Descriptor.Path} unlinked from dataset {dataset.Descriptor.DatasetId}.");
// If the dataset files are already loaded, refresh them
if (DatasetFiles.ContainsKey(dataset.Descriptor.DatasetId))
{
_ = GetFilesAsync(dataset.Descriptor.DatasetId);
}
}
catch (Exception e)
{
Debug.LogError($"Failed to unlink file: {file.Descriptor.Path}. {e}");
}
}
The code snippet does the following:
- Unlinks a file from a dataset.
- Prints a success message to the console on success or an error message if the unlinking fails.
Add a UI to create files
Add a UI to create files as follows:
- In your Unity Project window, go to Assets > Scripts.
- Select and hold the
Assets/Scripts
folder. - Go to Create > C# Script.
- Name your script
UseCaseFileCreationExampleUI
. - Open the
UseCaseFileCreationExampleUI
script you created in the previous step and replace the contents of the file with the following code sample:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Unity.Cloud.Assets;
using Unity.Cloud.Common;
using UnityEngine;
public class UseCaseFileCreationExampleUI : IAssetManagementUI
{
readonly AssetManagementBehaviour m_Behaviour;
public UseCaseFileCreationExampleUI(AssetManagementBehaviour behaviour)
{
m_Behaviour = behaviour;
}
public void OnGUI() { }
}
- In the same script, replace the
OnGUI
function with the following code:
static readonly GUILayoutOption k_DefaultButtonSize = GUILayout.Width(60);
IAsset m_CurrentAsset;
Vector2 m_DatasetsScrollPosition;
Dictionary<DatasetId, bool> m_Expanded = new();
static readonly string[] m_OverrideActions = {"None", "Replace", "Reupload"};
int m_OverrideFileActionIndex;
public void OnGUI()
{
if (!m_Behaviour.IsProjectSelected) return;
if (m_CurrentAsset != m_Behaviour.CurrentAsset)
{
m_CurrentAsset = m_Behaviour.CurrentAsset;
_ = m_Behaviour.GetDatasets();
}
if (m_CurrentAsset == null)
{
GUILayout.Label(" ! No asset selected !");
return;
}
GUILayout.BeginVertical();
if (GUILayout.Button("Refresh", k_DefaultButtonSize))
{
_ = m_Behaviour.GetDatasets();
}
GUILayout.Space(5);
if (m_Behaviour.Datasets == null)
{
GUILayout.Label("Loading datasets...");
GUILayout.EndVertical();
return;
}
DisplayDatasets(m_Behaviour.Datasets?.ToArray());
GUILayout.EndVertical();
}
void DisplayDatasets(IReadOnlyCollection<IDataset> datasets)
{
if (datasets.Count == 0)
{
GUILayout.Label("No datasets.");
return;
}
#if UNITY_EDITOR
GUILayout.Label("File upload options:");
m_OverrideFileActionIndex = GUILayout.SelectionGrid(m_OverrideFileActionIndex, m_OverrideActions, 3, GUILayout.Width(240));
GUILayout.Space(10);
#endif
m_DatasetsScrollPosition = GUILayout.BeginScrollView(m_DatasetsScrollPosition, GUILayout.ExpandHeight(true));
foreach (var dataset in datasets)
{
DisplayDataset(dataset);
GUILayout.Space(10);
}
GUILayout.EndScrollView();
}
void DisplayDataset(IDataset dataset)
{
GUILayout.BeginHorizontal();
GUILayout.Label(dataset.Name + " (" + dataset.Status + ")");
GUILayout.Space(5);
TryUploadFolder(dataset);
TryUploadFile(dataset);
var expanded = m_Expanded.GetValueOrDefault(dataset.Descriptor.DatasetId);
if (GUILayout.Button(expanded ? "-" : "+", GUILayout.Width(20)))
{
expanded = !expanded;
m_Expanded[dataset.Descriptor.DatasetId] = expanded;
if (!expanded)
{
m_Behaviour.DatasetFiles.Remove(dataset.Descriptor.DatasetId);
}
}
GUILayout.EndHorizontal();
if (expanded)
{
GUILayout.BeginHorizontal();
GUILayout.Space(25);
DisplayFiles(dataset);
GUILayout.EndHorizontal();
}
if (m_SelectedFile != null)
{
m_WindowRect = GUILayout.Window(0, m_WindowRect, DisplayWindow, "Select files to link");
}
}
void TryUploadFolder(IDataset dataset)
{
#if UNITY_EDITOR
if (GUILayout.Button("Upload folder", GUILayout.Width(90)))
{
var folderPath = UnityEditor.EditorUtility.OpenFolderPanel("Folder to upload", "Assets", string.Empty);
if (!string.IsNullOrEmpty(folderPath))
{
_ = m_Behaviour.UploadFolderAsync(dataset, folderPath, m_OverrideActions[m_OverrideFileActionIndex]);
}
}
#endif
}
void TryUploadFile(IDataset dataset)
{
#if UNITY_EDITOR
if (GUILayout.Button("Upload file", GUILayout.Width(90)))
{
var filePath = UnityEditor.EditorUtility.OpenFilePanel("File to upload", "Assets", string.Empty);
var folderPath = filePath[..filePath.LastIndexOf(Path.DirectorySeparatorChar)];
if (!string.IsNullOrEmpty(filePath))
{
_ = m_OverrideFileActionIndex switch
{
1 => m_Behaviour.ReplaceFile(dataset, filePath, folderPath),
2 => m_Behaviour.ReuploadFile(dataset, filePath, folderPath),
_ => m_Behaviour.UploadFile(dataset, filePath, folderPath)
};
}
}
#endif
}
void DisplayFiles(IDataset dataset)
{
if (!m_Behaviour.DatasetFiles.ContainsKey(dataset.Descriptor.DatasetId))
{
_ = m_Behaviour.GetFilesAsync(dataset.Descriptor.DatasetId);
}
var files = m_Behaviour.DatasetFiles.GetValueOrDefault(dataset.Descriptor.DatasetId);
if (files == null)
{
GUILayout.Label("Loading files...");
return;
}
var enumerable = files.ToArray();
if (!enumerable.Any())
{
GUILayout.Label("No files.");
return;
}
GUILayout.BeginVertical();
foreach (var file in enumerable)
{
DisplayFile(dataset, file);
}
GUILayout.EndVertical();
}
void DisplayFile(IDataset dataset, IFile file)
{
GUILayout.BeginHorizontal();
var size = file.SizeBytes < 1000 ? "<1" : MathF.Round(file.SizeBytes / 1000f).ToString("F0");
GUILayout.Label($"{file.Descriptor.Path} ({size} KB)");
if (GUILayout.Button("Link to", k_DefaultButtonSize))
{
m_WindowRect = new Rect(Screen.width * 0.4f, Screen.height * 0.4f, Screen.width * 0.2f, Screen.height * 0.2f);
m_SelectedFile = file;
m_AvailableDatasets = m_Behaviour.Datasets?.Where(f => !m_SelectedFile.LinkedDatasets.Contains(dataset.Descriptor)).ToList();
}
if (GUILayout.Button("Unlink", k_DefaultButtonSize))
{
_ = m_Behaviour.UnlinkFile(dataset, file);
}
GUILayout.EndHorizontal();
}
Rect m_WindowRect;
IFile m_SelectedFile;
List<IDataset> m_AvailableDatasets;
void DisplayWindow(int windowId)
{
GUILayout.BeginVertical();
GUILayout.Label($"Link {m_SelectedFile.Descriptor.Path} to:");
if (m_AvailableDatasets.Count == 0)
{
GUILayout.Label(" ! No datasets to link to !");
}
else
{
for (var i = 0; i < m_AvailableDatasets.Count; ++i)
{
GUILayout.BeginHorizontal();
GUILayout.Label(m_AvailableDatasets[i].Name);
if (GUILayout.Button("Link", k_DefaultButtonSize))
{
_ = m_Behaviour.LinkFile(m_AvailableDatasets[i], m_SelectedFile);
m_AvailableDatasets.RemoveAt(i);
// Force a refresh of the dataset files, including the already linked ones of the selected one
foreach (var linkedDatasetId in m_SelectedFile.LinkedDatasets.Select(d => d.DatasetId))
{
m_Behaviour.DatasetFiles.Remove(linkedDatasetId);
m_Expanded.Remove(linkedDatasetId);
}
m_Behaviour.DatasetFiles.Remove(m_AvailableDatasets[i].Descriptor.DatasetId);
m_Expanded.Remove(m_AvailableDatasets[i].Descriptor.DatasetId);
GUILayout.EndHorizontal();
break;
}
GUILayout.EndHorizontal();
}
}
GUILayout.Space(10);
if (GUILayout.Button("Close", k_DefaultButtonSize))
{
m_SelectedFile = null;
m_AvailableDatasets = null;
}
GUILayout.EndVertical();
}
- Open the
AssetManagementUI
script you created as described in Get started with Asset SDK and replace the contents of theAwake
function with the following code:
m_UI.Add(new OrganizationSelectionExampleUI(m_Behaviour));
m_UI.Add(new ProjectSelectionExampleUI(m_Behaviour));
m_UI.Add(new AssetSelectionExampleUI(m_Behaviour));
m_UI.Add(new UseCaseFileCreationExampleUI(m_Behaviour));
The code snippet does the following:
- Provides a UI button to refresh the list of files within each dataset.
- Provides UI buttons to trigger the creation of a new file within a dataset.
- Provides a UI button to link an existing file to a dataset.
- Provides a UI button for each existing file to unlink it from its dataset.
Going further
Update and download files
See the Download and manage files use case for more information.
Replace uploaded file content
See the Replace uploaded file content use case for more information.