utilcn logoutilcn
Storage

generatePresignedUploadUrl

Generate a presigned URL for secure file uploads to S3-compatible storage

Backend Implementation Required

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.

CORS Configuration Required

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.

Usage

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

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

Installation

pnpm dlx shadcn@latest add https://utilcn.dev/r/generate-presigned-upload-url.json
bunx --bun shadcn@latest add https://utilcn.dev/r/generate-presigned-upload-url.json
npx shadcn@latest add https://utilcn.dev/r/generate-presigned-upload-url.json
yarn shadcn@latest add https://utilcn.dev/r/generate-presigned-upload-url.json

Parameters

ParameterTypeDescriptionDefault
fileNamestringThe original filename with extension-
contentLengthnumberThe size of the file in bytes-
expiresInnumberURL expiration time in seconds3600

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

uploadFile

Generate presigned URLs for secure file uploads to cloud storage