golang rest api to upload multiple images

rest api to upload image in Golang

Home » Programming Language » Golang » rest api to upload image in Golang
golang rest api to upload multiple images

In this article we are going to learn about how we can create a rest api to upload image, we’ll also going to make sure that our api will handle multiple image upload as well and we are also going to set the file upload limit so it won’t make our system unstable, because long file upload can cause the system failure issue so it’s better to add a limit for the file, and we are going to make sure that we only accept image so we also need to add file type checking as well for our api, if you want images and docs from the same api you can remove the type checking filter and api is ready to handle multiple type files.

Steps we need to follow to make our rest api to upload images.

  • Create a Repository.
  • Setup go.mod.
  • Create main.go file
  • Setup http server using gorilla mux package.
  • Create the api.
  • Connect the rest api with http handler.
  • Test the api.

Let’s follow the steps one by one to create the successful rest api to upload images.

1. Create a Repository for our rest api

First we need to create the folder where we can set up our codebase.

Create a folder called upload-images-rest-api. Below is the command to create a directory/folder.

$ mkdir upload-images-rest-api

2. Setup go.mod

We also need to setup the go.mod file in the upload-images-rest-api folder, so we can use our code as a package/module, below is the command to generate the go.mod file.

$ go mod init upload-images-rest-api

If you are new to Go then you might need to understand the go packaging first to understand what this command does.

3. Create main.go file

Inside your upload-images-rest-api folder create another file called main.go.

$ touch main.go

as now we have also created our main.go file now we can start development of our http server.

4. Setup http server using gorilla mux package

For creating the server we are going to use the gorilla mux package so first we need to download the package first then we can start the development.

Below is the command to download the gorilla mux package

$ go get -u github.com/gorilla/mux

After the installation of gorilla mux we can start developing the code.

1. Create a main function.

First we need to write the boiler plate code of Go, below is the code.

package main

func main() {

}

As of now it’s a boiler plate code that’s why we don’t have any package loaded yet.

2. Create HTTP Server.

Below is the code of the http server.

package main

import (
    "log"
    "net/http"

    "github.com/gorilla/mux"
)

const PORT = "8080"

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/ping", nil).Methods("GET")
    r.HandleFunc("/upload", nil).Methods("POST")

    log.Printf("Server is running on http://localhost:%s", PORT)
    log.Println(http.ListenAndServe(":"+PORT, r))
}

First we added the PORT variable in which we want our server to run which is 8080, Inside the main function we have called the mux.NewRouter function of our gorilla mux package and then we have created our routes as mentioned in the code and also attached their methods as well, as you can see we have two API, first /ping, and it is to check if our server is alive or not and second /upload and it is for our main work to upload images, then we’re passing our mux router to http.ListenAndServe function and also passing the port on we want our server to run.

If you try to run the server and go to test any of the api you’ll get the error like this because we just passed the path but for the provided path there is no handler which can read that handle that request.

2022/11/27 19:16:00 http: panic serving [::1]:56663: runtime error: invalid memory address or nil pointer dereference
goroutine 18 [running]:
net/http.(*conn).serve.func1()
        C:/Program Files/Go/src/net/http/server.go:1850 +0xbf
panic({0xe08000, 0x1025de0})
        C:/Program Files/Go/src/runtime/panic.go:890 +0x262
net/http.HandlerFunc.ServeHTTP(0xc000148000?, {0xebd248?, 0xc0001360e0?}, 0x800?)
        C:/Program Files/Go/src/net/http/server.go:2109 +0x1e
github.com/gorilla/mux.(*Router).ServeHTTP(0xc000130000, {0xebd248, 0xc0001360e0}, 0xc000096100)
        C:/Users/KDSINGH/go/pkg/mod/github.com/gorilla/mux@v1.8.0/mux.go:210 +0x1cf
net/http.serverHandler.ServeHTTP({0xc000088120?}, {0xebd248, 0xc0001360e0}, 0xc000096100)
        C:/Program Files/Go/src/net/http/server.go:2947 +0x30c
net/http.(*conn).serve(0xc00009e000, {0xebd680, 0xc000073320})
        C:/Program Files/Go/src/net/http/server.go:1991 +0x607
created by net/http.(*Server).Serve
        C:/Program Files/Go/src/net/http/server.go:3102 +0x4db

To solve this issue let’s create a http handler for our /ping api which will be used as an api heartbeat to check if the server is running or not.

Let’s add the handler for the /ping route:

func Ping(w http.ResponseWriter, r *http.Request) {
    answer := map[string]interface{}{
        "messageType": "S",
        "message":     "",
        "data":        "PONG",
    }
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(200)
    json.NewEncoder(w).Encode(answer)
}

We have added the Ping function to handle the /ping route and inside the Ping function we have added our response structure as map[string]interface so we can add dynamic response as we want, we’re not dependent on struct. We have added messageType, message and data as our response. We going to use the same response json for our /upload api expect that data will be going to a struct with multiple fields.

Next we write our header content type and http code and then encoding and directly returning back our response as json.

3. Add Ping function into handler

Now we have our Ping function. Let’s add this in our /ping handler.

r.HandleFunc(“/ping”, Ping).Methods(“GET”)

Now we have added our Ping handler as well. Let’s test the API.

You can test the api using postman or your browser as ping doesn’t accept any parameter so it can be test on the browser as well.

Postman:

Postman-PING

Browser:

browser-PING

/ping is working as expected, now it’s time to implement our main handler as well.

4. Create the api to upload images

Below is the full code of upload image rest api.

// handler to handle the image upload
func UploadImages(w http.ResponseWriter, r *http.Request) {
    // 32 MB is the default used by FormFile() function
    if err := r.ParseMultipartForm(BULK_FILE_SIZE); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    // Get a reference to the fileHeaders.
    // They are accessible only after ParseMultipartForm is called
    files := r.MultipartForm.File["file"]

    var errNew string
    var http_status int

    for _, fileHeader := range files {
        // Open the file
        file, err := fileHeader.Open()
        if err != nil {
            errNew = err.Error()
            http_status = http.StatusInternalServerError
            break
        }

        defer file.Close()

        buff := make([]byte, 512)
        _, err = file.Read(buff)
        if err != nil {
            errNew = err.Error()
            http_status = http.StatusInternalServerError
            break
        }

        // checking the content type
        // so we don't allow files other than images
        filetype := http.DetectContentType(buff)
        if filetype != "image/jpeg" && filetype != "image/png" && filetype != "image/jpg" {
            errNew = "The provided file format is not allowed. Please upload a JPEG,JPG or PNG image"
            http_status = http.StatusBadRequest
            break
        }

        _, err = file.Seek(0, io.SeekStart)
        if err != nil {
            errNew = err.Error()
            http_status = http.StatusInternalServerError
            break
        }

        err = os.MkdirAll("./uploads", os.ModePerm)
        if err != nil {
            errNew = err.Error()
            http_status = http.StatusInternalServerError
            break
        }

        f, err := os.Create(fmt.Sprintf("./uploads/%d%s", time.Now().UnixNano(), filepath.Ext(fileHeader.Filename)))
        if err != nil {
            errNew = err.Error()
            http_status = http.StatusBadRequest
            break
        }

        defer f.Close()

        _, err = io.Copy(f, file)
        if err != nil {
            errNew = err.Error()
            http_status = http.StatusBadRequest
            break
        }
    }
    message := "file uploaded successfully"
    messageType := "S"

    if errNew != "" {
        message = errNew
        messageType = "E"
    }

    if http_status == 0 {
        http_status = http.StatusOK
    }

    resp := map[string]interface{}{
        "messageType": messageType,
        "message":     message,
    }
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http_status)
    json.NewEncoder(w).Encode(resp)
}

We have added our new handler UploadImages and added the check for limited size data upload for our /upload endpoint, we’re passing the BULK_FILE_SIZE into r.ParseMultipartForm function.

In next step we’re getting the all uploaded file using r.MultipartForm.File[“file”] and it’s giving us the map[string][]*multipart.FileHeader on which we’re iterating our loop sequentially.

Inside the loop first we’re opening the file using fileHeader.Open() and processing the returned information file, next we’re reading the opened file information in chunks buff := make([]byte, 512) using file.Read(buff) function: we’re passing our opened file to Read method and also passing the bytes we need to read from the opened file.

After reading small a chunk from a file we’re passing that chunks http.DetectContentType function and it’s returning back the file type and in next step we’re checking file type, we’re only accepting JPEG, JPG and PNG images.

In next step we’re calling file.Seek(0, io.SeekStart) for seeking image data from given offset to whence, then we’re creating the uploads folder in the root level of the project, after creating folder we’re creating the file where we can save the image data we have opened, and in the next we’re calling io.Copy(f, file) and passing the data file into our newly created file f.

In the end of the function we’re just processing the request and we need if the function got any error processing the image then it’ll return the error otherwise it’ll return back the successful message as type json response.

5. Connect the rest api with http handler

Now we have our handler for /upload api but it’s not connected yet so let’s connect it and then we’ll test our code. We just need to add the UploadImages function as a second argument into our /upload handlefunc.

r.HandleFunc(“/upload”, UploadImages).Methods(“POST”)

6. Test the Rest API.

We have connected our handlers with our routers, It’s time to test the /upload route:

Postman-upload-rest-api
uploaded-files-from-upload-rest-api

your comments are appreciated and if you wants to see your articles on this platform then please shoot a mail at this address kusingh@programmingeeksclub.com

Thanks for reading

Join Our Newsletter!

Join our newsletter to get our latest ebook "Ultimate JavaScript Cheat-Sheet", and Tips, Articles..

We don’t spam! Read our privacy policy for more info.

Join Our Newsletter!

Join our newsletter to get our latest ebook "Ultimate JavaScript Cheat-Sheet", and Tips, Articles..

We don’t spam! Read our privacy policy for more info.

Leave a Comment

Your email address will not be published. Required fields are marked *


The reCAPTCHA verification period has expired. Please reload the page.