Use case: Finalize thumbnail upload for an annotation
Before you start
Before you finalize a thumbnail upload, ensure you have completed the following prerequisites:
- Thumbnail Uploaded: You must have already uploaded a thumbnail file to the pre-signed URL obtained from
ReadThumbnailUploadUrlAsync. - Project Access: Ensure you have the necessary permissions to modify annotations in the target project.
- Upload Verification: Verify that your upload to the pre-signed URL completed successfully (HTTP 200/204 response).
- Set Up SDK Environment: Confirm that your Unity environment is correctly configured with the Collaboration SDK.
How do I...?
Finalize a thumbnail upload
To complete the thumbnail upload process and make the thumbnail visible, follow these steps:
- Complete Upload: Ensure you have successfully uploaded the thumbnail image to the pre-signed URL.
- Prepare Identifiers: Have your project and annotation identifiers ready.
- Call Finalize: Use the SDK to finalize the upload, which registers the thumbnail with the annotation.
- Verify: Confirm that the annotation now shows
HasThumbnail = true.
Example:
var projectId = new ProjectId("87cf845f-8ca7-4b2f-9cc1-c3d731335810");
var annotationId = new AnnotationId("a1b2c3d4-e5f6-7890-abcd-ef1234567890");
var annotationReference = new annotationReference(projectId, annotationId);
// After successfully uploading to the pre-signed URL, finalize the upload
await annotationManagement.FinalizeThumbnailUploadAsync(
annotationReference,
cancellationToken);
Debug.Log("Thumbnail upload finalized successfully");
// The annotation now has a thumbnail available
Complete thumbnail upload workflow with error handling
Here's the full workflow from upload to finalization with comprehensive error handling:
public async Task<bool> UploadAndFinalizeThumbnail(
ProjectId projectId,
AnnotationId annotationId,
byte[] imageData)
{
string uploadUrl = null;
var annotationReference = new annotationReference(projectId, annotationId);
try
{
// Step 1: Get upload URL
ReadThumbnailUploadUrlResult urlResult = await annotationManagement.ReadThumbnailUploadUrlAsync(
annotationReference,
cancellationToken);
uploadUrl = urlResult.Url;
Debug.Log("Upload URL obtained");
// Step 2: Upload the image
using (var httpClient = new HttpClient())
{
var content = new ByteArrayContent(imageData);
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/png");
HttpResponseMessage uploadResponse = await httpClient.PutAsync(uploadUrl, content);
if (!uploadResponse.IsSuccessStatusCode)
{
Debug.LogError($"Upload to pre-signed URL failed: {uploadResponse.StatusCode}");
return false;
}
Debug.Log("Image uploaded successfully");
}
// Step 3: Finalize the upload
await annotationManagement.FinalizeThumbnailUploadAsync(
annotationReference,
cancellationToken);
Debug.Log("Thumbnail finalized successfully");
return true;
}
catch (Exception ex)
{
Debug.LogError($"Failed to upload and finalize thumbnail: {ex.Message}");
return false;
}
}
Verify thumbnail after finalization
To confirm that the thumbnail is now available on the annotation:
var annotationReference = new annotationReference(projectId, annotationId);
// Upload and finalize the thumbnail
await UploadAndFinalizeThumbnail(annotationReference, imageData);
// Read the annotation to verify the thumbnail is present
var annotationResult = await annotationManagement.ReadAnnotationAsync(
annotationReference,
cancellationToken);
if (annotationResult.Annotation.HasThumbnail)
{
Debug.Log("Thumbnail is now available on the annotation");
// You can now get the download URL to display the thumbnail
var downloadResult = await annotationManagement.ReadThumbnailDownloadUrlAsync(
annotationReference,
cancellationToken);
Debug.Log($"Thumbnail download URL: {downloadResult.Url}");
}
else
{
Debug.LogWarning("Thumbnail finalization may not have completed yet");
}
Handle finalization failures
To handle cases where finalization might fail:
public async Task FinalizeThumbnailWithRetry(
ProjectId projectId,
AnnotationId annotationId,
int maxRetries = 3)
{
int retryCount = 0;
var annotationReference = new annotationReference(projectId, annotationId);
while (retryCount < maxRetries)
{
try
{
await annotationManagement.FinalizeThumbnailUploadAsync(
annotationReference,
cancellationToken);
Debug.Log("Thumbnail finalized successfully");
return;
}
catch (Exception ex)
{
retryCount++;
if (retryCount >= maxRetries)
{
Debug.LogError($"Failed to finalize after {maxRetries} attempts: {ex.Message}");
throw;
}
Debug.LogWarning($"Finalization attempt {retryCount} failed, retrying...");
await Task.Delay(1000 * retryCount); // Exponential backoff
}
}
}
Update thumbnail (replace existing)
To replace an existing thumbnail with a new one:
public async Task ReplaceThumbnail(
ProjectId projectId,
AnnotationId annotationId,
byte[] newImageData)
{
Debug.Log("Replacing existing thumbnail...");
// The process is the same: get new upload URL, upload, finalize
// The new thumbnail will replace the old one
var annotationReference = new annotationReference(projectId, annotationId);
// Step 1: Get new upload URL
ReadThumbnailUploadUrlResult urlResult = await annotationManagement.ReadThumbnailUploadUrlAsync(
annotationReference,
cancellationToken);
// Step 2: Upload new image
using (var httpClient = new HttpClient())
{
var content = new ByteArrayContent(newImageData);
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/png");
await httpClient.PutAsync(urlResult.Url, content);
}
// Step 3: Finalize - this replaces the old thumbnail
await annotationManagement.FinalizeThumbnailUploadAsync(
annotationReference,
cancellationToken);
Debug.Log("Thumbnail replaced successfully");
}
Understanding finalization
The finalization step is crucial in the thumbnail upload workflow:
- Registration: Finalization registers the uploaded file with the annotation system
- Visibility: The thumbnail becomes visible only after finalization
- Validation: The server validates that a file was uploaded to the expected location
- Metadata Update: The annotation's
HasThumbnailproperty is set totrue - Atomic Operation: Finalization ensures the thumbnail is fully available or not at all
Why finalization is required
- Two-Phase Upload: Separating upload and finalization allows for validation and prevents incomplete uploads
- Direct Storage Upload: Since uploads go directly to cloud storage (not through Unity servers), finalization notifies Unity that the upload completed
- Consistency: Ensures thumbnails are only shown when they're fully available
- Error Recovery: If upload fails, no finalization occurs, keeping the annotation state consistent
Important notes
- Must Follow Upload: Always call finalize immediately after successful upload
- Idempotent: Calling finalize multiple times for the same upload is safe
- No Partial State: Without finalization, the uploaded file won't be associated with the annotation
- Timeout Considerations: Finalize promptly after upload; delayed finalization may fail if upload URLs expire
- Error Handling: Always handle finalization errors and provide user feedback
For related operations, see documentation on getting upload URLs and downloading thumbnails.
See the API documentation for more details on the FinalizeThumbnailUploadAsync method.