Presigned Uploads
Overview
A presigned upload URL is a secure, temporary link that grants your application write-only permission to upload a file directly to a specific location in cloud storage (S3). This is the recommended method for getting assets into the Que system, especially from client-side applications like web browsers or mobile apps.
What, Why, and How
-
What is a Presigned Upload? It's a two-step process: first, you ask the Que SDK for a special upload URL. Second, your application uses that URL to upload the file directly to S3.
-
Why use Presigned Uploads?
- Security: Your API keys and cloud credentials are never exposed to the client. The URL is temporary and restricted to a single upload.
- Performance: Large files bypass your server and go straight to S3, reducing load and bandwidth on your infrastructure.
- Scalability: It leverages the highly scalable and reliable infrastructure of S3 for file uploads.
-
How does it work? The workflow involves three simple steps: request a URL, upload the file, and then use the returned object key to reference the asset in a sign or verify call.
The Upload Workflow
Request a Presigned URL
First, call the SDK's function to generate a presigned URL. This tells Que you intend to upload a file.
import { Que } from "que-sdk";
const que = new Que({
apiKeyAuth: process.env["QUE_API_KEY_AUTH"] ?? "",
});
async function getUploadUrl() {
try {
const presignResponse = await que.getPresignedUrl({
filename: "my-asset.jpg",
});
console.log('Received presigned URL:', presignResponse.uploadUrl);
console.log('Use this key for processing:', presignResponse.key);
return presignResponse;
} catch (error) {
console.error('Failed to get presigned URL:', error);
}
}
getUploadUrl();
import os
from que_media import Que
with Que(
api_key_auth=os.getenv("QUE_API_KEY_AUTH", ""),
) as que:
try:
response = que.get_presigned_url(filename="my-asset.jpg")
print(f"Received presigned URL: {response.upload_url}")
print(f"Use this key for processing: {response.key}")
except Exception as e:
print(f"Failed to get presigned URL: {e}")
package main
import (
"context"
"fmt"
"os"
"github.com/que-platform/que-sdk-go"
)
func main() {
client := quesdk.NewClient(os.Getenv("QUE_API_KEY"))
resp, err := client.GetPresignedURL(context.Background())
if err != nil {
fmt.Printf("Failed to get presigned URL: %v\n", err)
return
}
fmt.Printf("Received presigned URL: %s\n", resp.URL)
fmt.Printf("Use this key for processing: %s\n", resp.Key)
}
// The Que SDK for Rust is not yet available.
// The following is a conceptual example.
use que_sdk::QueClient;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = QueClient::new(std::env::var("QUE_API_KEY")?);
let response = client.get_presigned_url().await?;
println!("Received presigned URL: {}", response.url);
println!("Use this key for processing: {}", response.key);
Ok(())
}
package hello.world;
import java.lang.Exception;
import org.openapis.openapi.Que;
import org.openapis.openapi.models.components.*;
import org.openapis.openapi.models.errors.ProblemResponseException;
import org.openapis.openapi.models.operations.GetPresignedUrlResponse;
public class PresignExample {
public static void main(String[] args) throws ProblemResponseException, ProblemResponseException, ProblemResponseException, Exception {
Que sdk = Que.builder()
.apiKeyAuth(System.getenv().getOrDefault("API_KEY_AUTH", ""))
.build();
GetPresignedUrlRequest req = GetPresignedUrlRequest.builder()
.filename("my-asset.jpg")
.build();
GetPresignedUrlResponse res = sdk.getPresignedUrl()
.request(req)
.call();
if (res.presignedUrlResponse().isPresent()) {
System.out.println("Received presigned URL: " + res.presignedUrlResponse().get().uploadUrl());
System.out.println("Use this key for processing: " + res.presignedUrlResponse().get().key());
}
}
}
<?php
declare(strict_types=1);
require 'vendor/autoload.php';
use Que;
use Que\Models\Components;
$sdk = Que\Que::builder()
->setSecurity(getenv('QUE_API_KEY_AUTH') ?: '')
->build();
try {
$request = new Components\GetPresignedUrlRequest(
filename: 'my-asset.jpg'
);
$response = $sdk->getPresignedUrl(
request: $request
);
if ($response->getPresignedUrlResponse !== null) {
echo "Received presigned URL: " . $response->getPresignedUrlResponse->uploadUrl . "\n";
echo "Use this key for processing: " . $response->getPresignedUrlResponse->key . "\n";
}
} catch (Exception $e) {
echo 'Failed: ' . $e->getMessage();
}
Response Breakdown
The response object contains two critical pieces of information:
url
: The temporary, secure URL to which you will upload your file.key
: The unique identifier (S3 object key) for your file once it's uploaded. You must save this key.
Upload the Asset File
Using the url
from the previous step, make an HTTP PUT
request with the raw file data as the request body.
It is crucial to set the Content-Type
header in your PUT
request to match the MIME type of the file you are uploading (e.g., image/jpeg
, video/mp4
).
// Upload file using the presigned URL
async function uploadFile(presignedUrl: string, file: File) {
try {
const response = await fetch(presignedUrl, {
method: 'PUT',
headers: {
'Content-Type': file.type, // e.g., 'image/jpeg'
},
body: file,
});
if (response.ok) {
console.log('File uploaded successfully');
} else {
console.error('Upload failed:', response.statusText);
}
} catch (error) {
console.error('Upload error:', error);
}
}
// Usage
const file = document.getElementById('fileInput').files[0];
uploadFile(presignResponse.url, file);
import requests
def upload_file(presigned_url: str, file_path: str, content_type: str):
try:
with open(file_path, 'rb') as file:
response = requests.put(
presigned_url,
data=file,
headers={'Content-Type': content_type}
)
if response.status_code == 200:
print("File uploaded successfully")
else:
print(f"Upload failed: {response.status_code}")
except Exception as e:
print(f"Upload error: {e}")
# Usage
upload_file(presign_response.url, "path/to/your/file.jpg", "image/jpeg")
package main
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
func uploadFile(presignedURL, filePath, contentType string) error {
file, err := os.Open(filePath)
if err != nil {
return err
}
defer file.Close()
var buf bytes.Buffer
writer := multipart.NewWriter(&buf)
writer.SetBoundary("")
part, err := writer.CreateFormFile("file", filePath)
if err != nil {
return err
}
_, err = io.Copy(part, file)
if err != nil {
return err
}
writer.Close()
req, err := http.NewRequest("PUT", presignedURL, &buf)
if err != nil {
return err
}
req.Header.Set("Content-Type", contentType)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode == 200 {
fmt.Println("File uploaded successfully")
} else {
fmt.Printf("Upload failed: %d\n", resp.StatusCode)
}
return nil
}
// Usage
err := uploadFile(presignResponse.URL, "path/to/your/file.jpg", "image/jpeg")
if err != nil {
fmt.Printf("Error: %v\n", err)
}
use reqwest;
use std::fs::File;
use std::io::Read;
async fn upload_file(presigned_url: &str, file_path: &str, content_type: &str) -> Result<(), Box<dyn std::error::Error>> {
let mut file = File::open(file_path)?;
let mut buffer = Vec::new();
file.read_to_end(&mut buffer)?;
let client = reqwest::Client::new();
let response = client
.put(presigned_url)
.header("Content-Type", content_type)
.body(buffer)
.send()
.await?;
if response.status().is_success() {
println!("File uploaded successfully");
} else {
println!("Upload failed: {}", response.status());
}
Ok(())
}
// Usage
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
upload_file(&presign_response.url, "path/to/your/file.jpg", "image/jpeg").await?;
Ok(())
}
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
import java.nio.file.Files;
public class FileUploader {
public static void uploadFile(String presignedUrl, String filePath, String contentType) {
try {
File file = new File(filePath);
byte[] fileBytes = Files.readAllBytes(file.toPath());
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(presignedUrl))
.header("Content-Type", contentType)
.PUT(HttpRequest.BodyPublishers.ofByteArray(fileBytes))
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
System.out.println("File uploaded successfully");
} else {
System.out.println("Upload failed: " + response.statusCode());
}
} catch (IOException | InterruptedException e) {
System.err.println("Upload error: " + e.getMessage());
}
}
// Usage
public static void main(String[] args) {
uploadFile(presignResponse.getUrl(), "path/to/your/file.jpg", "image/jpeg");
}
}
<?php
declare(strict_types=1);
require 'vendor/autoload.php';
use Que;
use Que\Models\Components;
$sdk = Que\Que::builder()
->setSecurity(getenv('QUE_API_KEY_AUTH') ?: '')
->build();
function uploadFile(string $presignedUrl, string $filePath, string $contentType): void {
$fileContent = file_get_contents($filePath);
$context = stream_context_create([
'http' => [
'method' => 'PUT',
'header' => "Content-Type: $contentType\r\n",
'content' => $fileContent
]
]);
$result = file_get_contents($presignedUrl, false, $context);
if ($result !== false) {
echo "File uploaded successfully\n";
} else {
echo "Upload failed\n";
}
}
// Usage
$presignedRequest = new Components\GetPresignedUrlRequest(
filename: 'my-asset.jpg'
);
$presignedResponse = $sdk->getPresignedUrl(
request: $presignedRequest
);
if ($presignedResponse->getPresignedUrlResponse !== null) {
uploadFile($presignedResponse->getPresignedUrlResponse->uploadUrl, 'path/to/your/file.jpg', 'image/jpeg');
}
?>
Use the Asset Key for Processing
After the upload is complete, the asset is ready for processing. Use the key
returned in Step 1 to construct an Asset Reference for a signing or verification call.
The S3 bucket will be your organization's designated upload bucket.
// Example of using the key in a subsequent call
const result = await que.signAsset({
asset: {
bucket: 'your-asset-upload-bucket',
key: presignResponse.key, // The key from Step 1
},
manifestJson: JSON.stringify({
title: "My Signed Asset",
assertions: [{
label: "stds.schema-org.CreativeWork",
data: {
"@context": "https://schema.org",
"@type": "CreativeWork",
"author": [{ "@type": "Person", "name": "Creator" }]
}
}]
}),
});