In this tutorial, we'll walk through the process of building a Gallery App using FastAPI as the backend API and React as the frontend framework. The app will allow users to upload, retrieve, and delete images stored in AWS S3. Let's get started!
Before we begin, make sure you have the following installed:
$ mkdir gallery-app-backend
$ cd gallery-app-backend
$ python -m venv venv
$ source venv/bin/activate
$ pip install fastapi uvicorn boto3
Create a new file main.py
and add the following code to set up FastAPI:
from fastapi import FastAPI, UploadFile, File, HTTPException
import boto3
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"], # Add the URL of your React frontend
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
This configuration enables CORS for your FastAPI backend, allowing requests from the specified origin. Make sure to replace "http://localhost:3000"
with the correct URL of your React frontend or the appropriate wildcard ("*"
) depending on your requirements.Implement the necessary endpoints for image upload, retrieval, and deletion. You can refer to the FastAPI documentation and AWS SDK (boto3) documentation for the implementation details.
from fastapi import FastAPI, UploadFile, File, HTTPException
import boto3
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"], # Add the URL of your React frontend
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# AWS S3 Configuration
AWS_ACCESS_KEY_ID = 'YOUR_ACCESS_KEY_ID'
AWS_SECRET_ACCESS_KEY = 'YOUR_SECRET_ACCESS_KEY'
AWS_REGION_NAME = 'YOUR_REION_NAME'
S3_BUCKET_NAME = 'YOUR_BUCKET_NAME
s3 = boto3.client(
's3',
aws_access_key_id=AWS_ACCESS_KEY_ID,
aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
region_name=AWS_REGION_NAME
)
bucket_name = S3_BUCKET_NAME
@app.post("/upload")
async def upload_image(file: UploadFile = File(...)):
# Upload the image to AWS S3
s3.upload_fileobj(file.file, bucket_name, file.filename)
return {"message": "Image uploaded successfully"}
@app.get("/images/{filename}")
async def get_image(filename: str):
try:
# Generate a pre-signed URL for accessing the image from AWS S3
response = s3.generate_presigned_url(
"get_object",
Params={"Bucket": S3_BUCKET_NAME, "Key": filename},
ExpiresIn=3600 # URL expiration time (in seconds)
)
return {"url": response}
except Exception as e:
raise HTTPException(status_code=404, detail="Image not found")
@app.delete("/images/{filename}")
async def delete_image(filename: str):
try:
# Delete the image from AWS S3
print(filename)
s3.delete_object(Bucket=S3_BUCKET_NAME, Key=f'{filename}')
# print(filename)
return {"message": "Image deleted successfully"}
except Exception as e:
raise HTTPException(status_code=404, detail="Image not found")
# @app.get("/images")
# def get_images():
# try:
# response = s3.list_objects_v2(Bucket=bucket_name,Prefix='uploads/admin/')
# images = []
# for obj in response.get("Contents", []):
# images.append({
# "key": obj["Key"],
# "url": f"https://{bucket_name}.s3.amazonaws.com/{obj['Key']}"
# })
# return {"images": images}
# except Exception as e:
# return {"error": str(e)}
@app.get("/images")
async def get_all_images():
try:
# List all objects (images) in the S3 bucket
response = s3.list_objects_v2(Bucket=bucket_name,Prefix='uploads/admin/')
files = response.get("Contents")
filename_test =[]
for file in files:
# print(f"file_name: {file['Key']}, size: {file['Size']}")
filename_test.append(file['Key'])
print(filename_test)
# Extract the list of image URLs
image_urls = []
for obj in response.get("Contents", []):
# print(obj)
# new_obj= obj['Key'].split('/')[-1]
# print(new_obj)
image_urls.append({'url':
s3.generate_presigned_url(
"get_object",
Params={"Bucket": S3_BUCKET_NAME, "Key": obj["Key"]},
ExpiresIn=3600 # URL expiration time (in seconds)
),'filename': obj['Key'].split('/')[-1]}
)
print(image_urls)
return {"image_urls": image_urls,'filename_test': filename_test}
except Exception as e:
raise HTTPException(status_code=500, detail="Failed to fetch images")
$ npx create-react-app gallery-app-frontend
$ cd gallery-app-frontend
Install required dependencies:
$ npm install axios react-dropzone
App.js
and add the following code to set up the basic structure of the Gallery App:
import React, { useState, useEffect } from "react";
import axios from "axios";
function GalleryApp() {
const [images, setImages] = useState([]);
useEffect(() => {
fetchImages();
}, []);
const fetchImages = async () => {
try {
// Make an HTTP GET request to the FastAPI server to fetch all images
const response = await axios.get("http://localhost:8000/images");
// {console.log(response)};
setImages(response.data.image_urls);
} catch (error) {
console.error("Error fetching images:", error);
alert("Error fetching images.");
}
};
const handleDelete = async (filename) => {
try {
// Make an HTTP DELETE request to the FastAPI server to delete the image
// filename = 'maxresdefault_1.jpg'
// {console.log(filename)};
await axios.delete(`http://localhost:8000/images/${encodeURIComponent(filename)}`);
alert("Image deleted successfully!");
// {console.log(filename)}
// Update the gallery state by removing the deleted image
setImages((prevImages) =>
prevImages.filter((image) => image.filename !== filename)
);
} catch (error) {
console.error("Error deleting image:", error);
alert("Error deleting image.");
}
};
return (
<div>
<h1>Gallery App</h1>
<hr />
<h2>Gallery</h2>
{images.map((image, index) => (
<div key={index}>
<img src={image.url} width="1000" height="600" alt={`Gallery Image ${index}`} />
{/* {console.log(image.filename)} */}
{'\n'}
<button onClick={() => handleDelete(image.filename)}>
Delete
</button>
</div>
))}
</div>
);
}
export default GalleryApp;
Implement the necessary components and logic for image upload, retrieval, and deletion in the Gallery App component. You can use libraries like axios
for making HTTP requests and react-dropzone
for handling image uploads.
Save the App.js
file.
Start the development server:
npm start
Make sure the backend FastAPI server is running before starting the frontend development server. You can start the backend server using the following command in a separate terminal:
uvicorn main:app --reload
You can access the web app at http://localhost:3000
in your browser. If you like this article, Please give thumbs up . Happy Coding !!!