Prerequisites
- Node.js
- npm, pnpm, or yarn (Bun recommend)
- AWS account with S3 bucket access
Step 1: Install Astro
Open a terminal and run:
npm create astro@latest s3-image-upload-api
Follow the interactive setup.Where will be asked for the project configuration, once done. Navigate into your project:
cd s3-image-upload-api
Start the development server:
npm run dev
If something went wrong, please follow the official Astro docs for installing. Here Install Astro
Step 2: Install AWS SDK and Configure S3 Client
Install the AWS SDK:
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
Create an .env
file in your project root and add:
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_REGION=your-region
AWS_BUCKET_NAME=your-bucket
Create an s3Client.js
file inside lib/
:
import { S3Client } from "@aws-sdk/client-s3";
export const s3Client = new S3Client({
region: process.env.AWS_REGION,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
},
});
Step 3: Generate a Signed URL
Create a function to generate signed URLs
import { GetObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { s3Client } from "./s3Client";
export async function getSignedUrlForFile(fileName) {
const command = new GetObjectCommand({
Bucket: process.env.AWS_BUCKET_NAME,
Key: fileName,
});
return await getSignedUrl(s3Client, command, { expiresIn: 3600 });
}
Step 4: Generate a Seting up endpoints
export const POST: APIRoute = async ({ request }) => {
const formData = await request.formData();
const file = formData.get("image") as File;
if (!file) {
return new Response(JSON.stringify({ error: "No file uploaded" }), {
status: 400,
headers: { "Content-Type": "application/json" },
});
}
const uniquePrefix = Date.now() + "-" + Math.round(Math.random() * 1e9)
const fileExtension = file.name.split(".").pop()
const fileName = `${uniquePrefix}.${fileExtension}`
const bytes = await file.arrayBuffer()
const fileBuffer = Buffer.from(bytes)
const putObjectParams = {
Bucket: process.env.AWS_BUCKET_NAME!,
Key: fileName,
Body: fileBuffer,
ContentType: file.type,
};
const command = new PutObjectCommand(putObjectParams);
const s3UploadResponse = await s3Client.send(command);
/* IMPORTANT: We are saving only a reference to the image (the S3 object key).
Since this is a private S3 bucket, you must configure your S3 bucket for
public or general access if you want to use direct URLs.
Alternatively, you can generate a pre-signed URL to grant temporary access.
Here, we handle a private S3 bucket, but since we define a limited time
for the valid link, it allows temporary access to the file. */
// const publicS3Url = await getPresignedUrl(fileName);
// Save only the S3 object key for future queries.
return new Response(JSON.stringify({ path: fileName }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
In Amazon S3, the term KEY refers to the resource's unique identifier, which is essentially the name of the file within the bucket. Here, we save this key for future queries.
Step 5: Use Signed URLs in Astro
Use the API in your Astro page:
---
let signedUrl = "";
const res = await fetch("/api/signed-url");
const data = await res.json();
signedUrl = data.url;
---
<a href={signedUrl} download>Download File</a>
Lastly if you need to consume the file or serve the url can call like this having a private buket:
export const GET: APIRoute = async ({ request }) => {
const key = new URL(request.url).searchParams.get('key')!;
if (!key) {
return new Response(JSON.stringify({
status: 400,
message: 'Missing key'
}))
}
try {
const srcFile = await getPresignedUrl(key)
return new Response(JSON.stringify({
srcFile
}))
} catch (error) {
return new Response(JSON.stringify({
status: 500,
message: `Error generating URL: ${error}`
}))
}
}
Conclusion
So if you followed the steps, you should able to see a link to your images and use it in your front end, the code is a messy right now maybe later will give a check. 🎉
Top comments (0)