Image upload using Golang and React
Tags: javascript, go, gofiber, react
Published: 2021-06-24 16:19:29

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

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

```
mkdir server 

cd server

```

Setup go environment

```
go mod init github.com/harshmangalam

```

install packages required for backend

```
go get  github.com/gofiber/fiber/v2

go get github.com/google/uuid


```

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


```go
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})
}



```


run `main.go` from `server`

```
go run main.go

```

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

![postman](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/172domaavut99icwr9v8.png)


## setup frontend

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

```

npx create-react-app reactjs

cd reactjs

```

install dependencies

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

```

`index.js`

```js

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


ReactDOM.render(

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


```




setup `App.js`


```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;



```



create new hook `useUpload` hook in `hooks` folder


`hooks/useUpload.js`

```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;




```


create `Upload.js` inside `components` folder


`components/Upload.js`


```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;



```

![1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i7zg2oh4atqwidyrsoww.png)





![2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2fw8lk8dblj00wknt31c.png)


![3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rcq8ngfer3sumv2mei31.png)



![4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bjrn71n4iaugq8ts9y0y.png)


![5](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vf716b54pfwvm2heeayz.png)



## Github Repo

### Backend

{% github harshmangalam/golang-react-image-upload-server %}

### Frontend

{% github harshmangalam/golang-react-image-upload-client %}