shadimage
utilcn
libgenerate-presigned-upload-urlsource: ok · hit

Generate Presigned Upload URL

Generate a presigned upload URL for a file.

npx shadcn add "https://utilcn.dev/r/generate-presigned-upload-url.json"
free

No selection tags yet — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light
@aws-sdk/client-s3@aws-sdk/s3-request-presigner

Files2 · source cached 10/14/2026, 2:55:30 PM

registry/default/storage/generate-presigned-upload-url.ts
import { PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

import { getS3Client } from '@/registry/default/storage/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';
  }
}
registry/default/storage/s3-client.ts
import { S3Client } from '@aws-sdk/client-s3';

let s3Client: S3Client | null = null;

export const getS3Client = (): S3Client => {
  if (s3Client) {
    return s3Client;
  }

  if (
    !process.env.S3_REGION ||
    !process.env.S3_ENDPOINT ||
    !process.env.S3_ACCESS_KEY ||
    !process.env.S3_SECRET_KEY
  ) {
    throw new Error('Missing S3 environment variables');
  }

  s3Client = new S3Client({
    region: process.env.S3_REGION,
    endpoint: process.env.S3_ENDPOINT,
    forcePathStyle: true, // Required for some S3-compatible services
    credentials: {
      accessKeyId: process.env.S3_ACCESS_KEY,
      secretAccessKey: process.env.S3_SECRET_KEY,
    },
  });

  return s3Client;
};

More from utilcn

Browse all components in this library →