Building a Gallery App with FastAPI and React in AWS S3

||
Posted 1 year, 4 months ago
||
Views 614
||
4 min read
0 reactions

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!

Prerequisites

Before we begin, make sure you have the following installed:

  • Python and pip
  • Node.js and npm
  • AWS account with S3 bucket set up
  • Basic knowledge of FastAPI and React

Setting Up the Backend with FastAPI

  1. Create a new FastAPI project by running the following commands:
    $ mkdir gallery-app-backend
    $ cd gallery-app-backend
    $ python -m venv venv
    $ source venv/bin/activate
    $ pip install fastapi uvicorn boto3
    
  2. 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.
  3. 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")

Setting Up the Frontend with React

  1. Create a new React project by running the following commands:
    $ npx create-react-app gallery-app-frontend
    $ cd gallery-app-frontend
    
  2. Install required dependencies:

    $ npm install axios react-dropzone
    
  3. Replace  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;
    
  4. 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.

  5. Save the App.js file.

  6. 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 yes . Happy Coding !!!


0 reactions

Discussion


Looking for Freelancing Jobs
Joined on April 15, 2020

Latest Videos