Skip to content

Instantly share code, notes, and snippets.

@mike-pete
Created February 24, 2026 04:58
Show Gist options
  • Select an option

  • Save mike-pete/1b2ffe5c0e9a0f87775d28890308ef0f to your computer and use it in GitHub Desktop.

Select an option

Save mike-pete/1b2ffe5c0e9a0f87775d28890308ef0f to your computer and use it in GitHub Desktop.
// upload-image.ts
// Upload a file to S3 or Cloudflare R2 using Bun's built-in S3 client.
// No third-party dependencies required.
//
// Usage:
// bun run upload-image.ts <path-to-file> [key]
//
// Arguments:
// <path-to-file> Local file to upload
// [key] Optional S3 object key (defaults to the filename)
//
// Environment variables (via .env):
// S3_ACCESS_KEY_ID Your access key
// S3_SECRET_ACCESS_KEY Your secret key
// S3_BUCKET Bucket name
// S3_ENDPOINT S3/R2 endpoint URL
// S3_REGION Region (default: "auto" for R2)
// S3_PUBLIC_URL Public base URL for uploaded objects
//
// Examples:
// bun run upload-image.ts ./photo.png
// bun run upload-image.ts ./photo.png images/photo.png
import { S3Client, file } from "bun";
const client = new S3Client({
accessKeyId: Bun.env.S3_ACCESS_KEY_ID!,
secretAccessKey: Bun.env.S3_SECRET_ACCESS_KEY!,
bucket: Bun.env.S3_BUCKET!,
endpoint: Bun.env.S3_ENDPOINT, // e.g. https://<account-id>.r2.cloudflarestorage.com
region: Bun.env.S3_REGION ?? "auto",
});
const imagePath = Bun.argv[2];
if (!imagePath) {
console.error("Usage: bun run upload-image.ts <path-to-image>");
process.exit(1);
}
const localFile = file(imagePath);
if (!(await localFile.exists())) {
console.error(`File not found: ${imagePath}`);
process.exit(1);
}
const key = Bun.argv[3] ?? localFile.name!.split("/").pop()!;
const bytes = await localFile.bytes();
console.log(`Uploading ${imagePath} → ${key} (${bytes.length} bytes)`);
await client.write(key, bytes, { type: localFile.type, acl: "public-read" });
const publicUrl = `${Bun.env.S3_PUBLIC_URL}/${key}`;
console.log(`Done: ${publicUrl}`);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment