šŸ“ Sign Up | šŸ” Log In

← Root | ↑ Up

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” │ šŸ“„ shadcn/directory/brennenrocks/utilcn/storage/generate-presigned-upload-url │ ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

╔══════════════════════════════════════════════════════════════════════════════════════════════╗
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘

title: generatePresignedUploadUrl description: Generate a presigned URL for secure file uploads to S3-compatible storage

<Callout title="Backend Implementation Required" type="error"> This function **must be implemented on your backend server** and cannot be safely exposed to the frontend. The function handles sensitive S3 credentials and should only be called from your server-side code. </Callout> <Callout title="CORS Configuration Required" type="error"> You **must configure CORS settings** on your S3 bucket or storage provider to allow your frontend domain to upload files using the generated presigned URLs. Without proper CORS configuration, uploads will fail with cross-origin errors. </Callout>

Usage

import { generatePresignedUploadUrl } from '@/lib/generate-presigned-upload-url';

const { uploadUrl, key, fileUrl } = await generatePresignedUploadUrl({
  fileName: file.name,
  contentLength: file.size,
  expiresIn: 3600
});

Installation

<InstallTabs component="generate-presigned-upload-url" />

Parameters

| Parameter | Type | Description | Default | |----------------|----------|------------------------------------------------|---------| | fileName | string | The original filename with extension | - | | contentLength| number | The size of the file in bytes | - | | expiresIn | number | URL expiration time in seconds | 3600 |

Returns

An object containing:

  • uploadUrl - The presigned URL for uploading the file
  • key - The unique key/path where the file will be stored
  • fileUrl - The public URL where the file will be accessible after upload

Environment Variables

This function requires the following environment variables:

S3_REGION=your-region
S3_ENDPOINT=your-s3-endpoint
S3_ACCESS_KEY=your-access-key
S3_SECRET_KEY=your-secret-key
S3_BUCKET_NAME=your-bucket-name
S3_PUBLIC_URL=your-public-url

Supported File Types

The function automatically detects content types for common file extensions:

  • Images: jpg, jpeg, png, gif, webp, svg
  • Documents: pdf, doc, docx
  • Other: json, txt
  • Default: application/octet-stream for unknown types

File Size Limits

  • Maximum file size: 10MB (customizeable)
  • Files exceeding this limit will throw an error

Dependencies

  • @aws-sdk/client-s3
  • @aws-sdk/s3-request-presigner

Implementation

import { PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

import { getS3Client } from '@/lib/s3-client';

const MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024; // 10MB

type PresignedUrlInput = {
  fileName: string;
  expiresIn?: number;
  contentLength: number;
};

export async function generatePresignedUploadUrl({
  fileName,
  expiresIn = 3600,
  contentLength,
}: PresignedUrlInput) {
  if (contentLength > MAX_FILE_SIZE_BYTES) {
    throw new Error('File size exceeds maximum allowed limit');
  }

  try {
    const fileExt = fileName.split('.').pop()?.toLowerCase() ?? '';
    const uniqueId = Date.now().toString();
    const key = fileExt ? `${uniqueId}.${fileExt}` : uniqueId;
    const contentType = getContentType(fileExt);

    const command = new PutObjectCommand({
      Bucket: process.env.S3_BUCKET_NAME,
      Key: key,
      ContentType: contentType,
      ContentLength: contentLength,
    });

    const uploadUrl = await getSignedUrl(getS3Client(), command, { expiresIn });
    const fileUrl = `${process.env.S3_PUBLIC_URL}/${key}`;

    return { uploadUrl, key, fileUrl };
  } catch (err) {
    console.error({ err }, 'Failed to generate presigned upload URL');
    throw new Error('Failed to generate upload URL');
  }
}

function getContentType(extension: string): string {
  switch (extension) {
    // Images
    case 'jpg':
    case 'jpeg':
      return 'image/jpeg';
    case 'png':
      return 'image/png';
    case 'gif':
      return 'image/gif';
    case 'webp':
      return 'image/webp';
    case 'svg':
      return 'image/svg+xml';

    // Documents
    case 'pdf':
      return 'application/pdf';
    case 'doc':
      return 'application/msword';
    case 'docx':
      return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';

    // Other common types
    case 'json':
      return 'application/json';
    case 'txt':
      return 'text/plain';

    default:
      return 'application/octet-stream';
  }
}

Frontend Integration

<Card title="uploadFile" href="/docs/storage/upload-file"> Generate presigned URLs for secure file uploads to cloud storage </Card>
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•

← Root | ↑ Up