DEV Community

Cover image for Image upload using Golang and React
Harsh Mangalam
Harsh Mangalam

Posted on • Updated on

Image upload using Golang and React

Golang is a blockbuster server side language in the field of efficiency and concurrency. If you are a Nodejs developer definitely you will come across express js for building your web api services. Gofiber is exactly like the express framework for golang and no doubt it booms with the efficiency of Fasthttp and golang.

In this blog post we will create a simple image upload server using gofiber and we will use reactjs for frontend to select image from file and upload to server.

we will use axios for http request to server and it is really awesome when we deal with implementing authentication and handling lots of api requests. It has lots of features which make life easy when dealing with api in react.

we will use chakra ui for designing material like button , images and layout it shins in Accessibility that directly effect better SEO.

library and tools we will use

  • golang
  • gofiber
  • reactjs
  • axios
  • chakra ui

Setup backend

create new directory and enter into it

mkdir go-react-image-upload

cd go-react-image-upload

Enter fullscreen mode Exit fullscreen mode

create a new directory server inside go-react-image-upload and enter into it

mkdir server 

cd server

Enter fullscreen mode Exit fullscreen mode

Setup go environment

go mod init github.com/harshmangalam

Enter fullscreen mode Exit fullscreen mode

install packages required for backend

go get  github.com/gofiber/fiber/v2

go get github.com/google/uuid


Enter fullscreen mode Exit fullscreen mode

uuid will help to generate unique id so that we can name our image easily and no two image will have same name.

create new go file main.go inside server and start writting code

package main

import (
    "fmt"
    "log"
    "os"
    "strings"

    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/fiber/v2/middleware/cors"
    "github.com/google/uuid"
)

func main() {
    // create new fiber instance  and use across whole app
    app := fiber.New()

    // middleware to allow all clients to communicate using http and allow cors
    app.Use(cors.New())

    // serve  images from images directory prefixed with /images
    // i.e http://localhost:4000/images/someimage.webp

    app.Static("/images", "./images")

    // handle image uploading using post request

    app.Post("/", handleFileupload)

    // delete uploaded image by providing unique image name

    app.Delete("/:imageName", handleDeleteImage)

    // start dev server on port 4000

    log.Fatal(app.Listen(":4000"))
}



func handleFileupload(c *fiber.Ctx) error {

    // parse incomming image file

    file, err := c.FormFile("image")

    if err != nil {
        log.Println("image upload error --> ", err)
        return c.JSON(fiber.Map{"status": 500, "message": "Server error", "data": nil})

    }

    // generate new uuid for image name 
    uniqueId := uuid.New()

    // remove "- from imageName"

    filename := strings.Replace(uniqueId.String(), "-", "", -1)

    // extract image extension from original file filename

    fileExt := strings.Split(file.Filename, ".")[1]

    // generate image from filename and extension
    image := fmt.Sprintf("%s.%s", filename, fileExt)

    // save image to ./images dir 
    err = c.SaveFile(file, fmt.Sprintf("./images/%s", image))

    if err != nil {
        log.Println("image save error --> ", err)
        return c.JSON(fiber.Map{"status": 500, "message": "Server error", "data": nil})
    }

    // generate image url to serve to client using CDN

    imageUrl := fmt.Sprintf("http://localhost:4000/images/%s", image)

    // create meta data and send to client

    data := map[string]interface{}{

        "imageName": image,
        "imageUrl":  imageUrl,
        "header":    file.Header,
        "size":      file.Size,
    }

    return c.JSON(fiber.Map{"status": 201, "message": "Image uploaded successfully", "data": data})
}


func handleDeleteImage(c *fiber.Ctx) error {
    // extract image name from params
    imageName := c.Params("imageName")

    // delete image from ./images
    err := os.Remove(fmt.Sprintf("./images/%s", imageName))
    if err != nil {
        log.Println(err)
        return c.JSON(fiber.Map{"status": 500, "message": "Server Error", "data": nil})
    }

    return c.JSON(fiber.Map{"status": 201, "message": "Image deleted successfully", "data": nil})
}



Enter fullscreen mode Exit fullscreen mode

run main.go from server

go run main.go

Enter fullscreen mode Exit fullscreen mode

Now our server is up and running we can test it using Postman

postman

setup frontend

come outside from server directory and generate reactjs project using create-react-app


npx create-react-app reactjs

cd reactjs

Enter fullscreen mode Exit fullscreen mode

install dependencies

npm i @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^4 axios

Enter fullscreen mode Exit fullscreen mode

index.js


import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';


ReactDOM.render(

  <App />
  ,
  document.getElementById('root')
);


Enter fullscreen mode Exit fullscreen mode

setup App.js

import { Box, ChakraProvider, Container } from "@chakra-ui/react";
import Axios from "axios";
import Upload from "./components/Upload";

Axios.defaults.baseURL = "http://localhost:4000";

function App() {
  return (
    <ChakraProvider>
      <Box
        minH="100vh"
        w="100%"
        bg="gray.200"
        display="flex"
        alignItems="center"
        justifyContent="center"
      >
        <Container maxWidth="container.xl">
          <Upload />
        </Container>
      </Box>
    </ChakraProvider>
  );
}

export default App;



Enter fullscreen mode Exit fullscreen mode

create new hook useUpload hook in hooks folder

hooks/useUpload.js



import { useState } from "react";
import axios from "axios";
import { useToast } from "@chakra-ui/react";

const useUpload = () => {
  const [image, setImage] = useState(null);
  const [loading, setLoading] = useState(false);

  const [uploadedImage, setUploadedImage] = useState(null);

  const toast = useToast();

  const handleChangeImage = (e) => {
    setImage(e.target.files[0]);
  };

  const handleUploadImage = async () => {
    try {
      setLoading(true);
      const formData = new FormData();
      formData.append("image", image);
      const res = await axios.post("/", formData);
      if (res.data.data) {
        console.log(res.data);
        setUploadedImage(res.data.data);
        toast({
          title: "Image Uploaded",
          description: res.data.message,
          status: "success",
          duration: 4000,
          isClosable: true,
        });
      }
    } catch (error) {
      console.log(error);
    } finally {
      setImage(null);
      setLoading(false);
    }
  };

  const handleRemoveImage = async () => {
    try {
      setLoading(true);

      const res = await axios.delete(`/${uploadedImage.imageName}`);
      if (res.data) {
        console.log(res.data);
        setUploadedImage(null);
        toast({
          title: "Image Deleted",
          description: res.data.message,
          status: "success",
          duration: 4000,
          isClosable: true,
        });
      }
    } catch (error) {
      console.log(error);
    } finally {
      setLoading(false);
    }
  };
  return {
    image,
    uploadedImage,
    loading,
    handleChangeImage,
    handleUploadImage,
    handleRemoveImage,
  };
};

export default useUpload;




Enter fullscreen mode Exit fullscreen mode

create Upload.js inside components folder

components/Upload.js



import { Button, Heading, VStack, Image, HStack, Tag } from "@chakra-ui/react";
import React from "react";
import { useRef } from "react";
import useUpload from "../hooks/useUpload";

function Upload() {
  const imageRef = useRef(null);
  const {
    loading,
    image,

    handleRemoveImage,
    handleChangeImage,
    handleUploadImage,
    uploadedImage,
  } = useUpload();
  return (
    <>
      <input
        style={{ display: "none" }}
        type="file"
        accept="image/*"
        ref={imageRef}
        onChange={handleChangeImage}
      />
      <VStack>
        <Heading>Image uploading using Golang and Reactjs</Heading>
        <Button
          onClick={() => imageRef.current.click()}
          colorScheme="blue"
          size="lg"
        >
          Select Image
        </Button>
      </VStack>

      {image && (
        <VStack my="4">
          <Image
            src={URL.createObjectURL(image)}
            width="300px"
            height="300px"
            alt="selected image..."
          />
          <Button
            onClick={handleUploadImage}
            variant="outline"
            colorScheme="green"
            isLoading={loading}
          >
            Upload
          </Button>
        </VStack>
      )}

      {uploadedImage && (
        <VStack my="4">
          <Image
            src={uploadedImage.imageUrl}
            width="300px"
            height="300px"
            alt={uploadedImage.imageName}
          />

          <HStack>
            <Tag variant="outline" colorScheme="blackAlpha">
              ~ {Math.floor(uploadedImage.size / 1024)} Kb
            </Tag>
            <Button
              variant="solid"
              colorScheme="red"
              onClick={handleRemoveImage}
              isLoading={loading}
            >
              Delete
            </Button>
          </HStack>
        </VStack>
      )}
    </>
  );
}

export default Upload;



Enter fullscreen mode Exit fullscreen mode

1

2

3

4

5

Github Repo

Backend

Frontend

Getting Started with Create React App

This project was bootstrapped with Create React App.

Available Scripts

In the project directory, you can run:

npm start

Runs the app in the development mode.
Open http://localhost:3000 to view it in the browser.

The page will reload if you make edits.
You will also see any lint errors in the console.

npm test

Launches the test runner in the interactive watch mode.
See the section about running tests for more information.

npm run build

Builds the app for production to the build folder.
It correctly bundles React in production mode and optimizes the build for the best performance.

The build is minified and the filenames include the hashes.
Your app is ready to be deployed!

See the section about deployment for more information.

npm run eject

Note: this is a one-way operation. Once you eject, you can’t go back!

If you…

Latest comments (3)

Collapse
 
hizbe profile image
Muhammad Hizbullah

hi @harshmangalam
how about passing variable image to next handler ?? like in node express ??
i hope you can show me.
thanks.

Collapse
 
irsalyunus profile image
irsal-yunus

hi harsh,
may i know the dependencies used for reactjs?
i am using environment node js version v17.1.0 and yarn version yarn version v1.22.15
info Current version: 0.1.0 I found bugs

Collapse
 
aceix profile image
the_aceix

Dependencies are always in package.json file
github.com/harshmangalam/golang-re...