NoCloud uses a two-step upload process:
- Get a signed URL from the Generate Signed URL endpoint
- Upload the file directly to the signed URL
There are two modes depending on whether you provide query parameters:
- Non-allocated: No query parameters — returns a signed URL. Upload with a POST request using form data.
- Pre-allocated: Pass
contentType and size as query parameters to pre-allocate storage — returns a signed URL along with mediaId and mediaUrl. Upload with a PUT request.
This approach allows for efficient, direct-to-storage uploads without proxying through your backend.
Simple Upload (Non-allocated)
# Step 1: Get signed URL
SIGNED_URL_RESPONSE=$(curl -s -X GET \
"https://api.nonefivem.com/cloud/storage/signed-url" \
-H "Authorization: Bearer your-api-key")
# Extract the signed URL
UPLOAD_URL=$(echo $SIGNED_URL_RESPONSE | jq -r '.url')
# Step 2: Upload file via POST with form data
curl -X POST "$UPLOAD_URL" \
-F "file=@./screenshot.png" \
-F 'metadata={"category":"screenshots"}'
Pre-allocated Upload
Pass contentType and size to pre-allocate storage. The response includes mediaId and mediaUrl so you know the file’s location before uploading.
# Step 1: Get signed URL with pre-allocation
SIGNED_URL_RESPONSE=$(curl -s -X GET \
"https://api.nonefivem.com/cloud/storage/signed-url?contentType=image/png&size=1048576" \
-H "Authorization: Bearer your-api-key")
# Extract the signed URL
UPLOAD_URL=$(echo $SIGNED_URL_RESPONSE | jq -r '.url')
# Step 2: Upload file to signed URL via PUT
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: image/png" \
--data-binary @./screenshot.png
When using the pre-allocated flow, you can attach metadata via query parameters:
const params = new URLSearchParams({
contentType: "image/png",
size: file.size.toString(),
// Metadata as JSON string
metadata: JSON.stringify({
oderId: "order_123",
visitorId: 456,
isPublic: true
})
});
const response = await fetch(
`https://api.nonefivem.com/cloud/storage/signed-url?${params}`,
{
headers: { Authorization: "Bearer your-api-key" }
}
);
Metadata constraints: - Maximum 10 keys - Key names: alphanumeric,
underscores, hyphens only (max 128 chars) - Values: string (max 512 chars),
number, or boolean - Total metadata size: max 2KB
Upload with Progress Tracking
For larger files, you may want to track upload progress. This example uses the pre-allocated flow:
async function uploadWithProgress(file, onProgress) {
// Get signed URL with pre-allocation
const { url, mediaId, mediaUrl } = await getSignedUrl(file);
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", (event) => {
if (event.lengthComputable) {
const percentComplete = (event.loaded / event.total) * 100;
onProgress(percentComplete);
}
});
xhr.addEventListener("load", () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve({ mediaId, mediaUrl });
} else {
reject(new Error(`Upload failed with status ${xhr.status}`));
}
});
xhr.addEventListener("error", () => reject(new Error("Upload failed")));
xhr.open("PUT", url);
xhr.setRequestHeader("Content-Type", file.type);
xhr.send(file);
});
}
// Usage
await uploadWithProgress(file, (progress) => {
console.log(`Upload progress: ${progress.toFixed(1)}%`);
});
Signed URLs expire after 15 minutes. Generate a new URL if the upload doesn’t
complete in time.