DEV Community

Gustavo Castillo
Gustavo Castillo

Posted on • Edited on

How to upload files with Golang and Ajax?

Uploading file(s) is a common functionality that we want to use in our websites, it could be for user's profile avatar, an image gallery, a cover post and so on, but What if you want to do it by using Ajax. Let's see how I solved this problem.

First of all let me give you the project structure:

public\ api.js app.js index.html style.css controllers\ file.go files\ 

Then let's create the index.html file inside public folder and write the following code:

<!DOCTYPE html> <html lang="es"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"> <link rel="stylesheet" href="styles.css"> <title>File upload using Ajax</title> </head> <body> <form action="/upload" method="post" enctype="multipart/form-data" class="uploadForm"> <input class="uploadForm__input" type="file" name="file" id="inputFile" accept="image/*"> <label class="uploadForm__label" for="inputFile"> <i class="fa fa-upload uploadForm__icon"></i> Select a file </label> </form> <div class="notification" id="alert"></div> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script src="api.js"></script> <script src="app.js"></script> </body> </html> 

Let's add some basic CSS styles and create the styles.css file inside public folder:

 body { margin: 0; padding: 0; width: 100%; height: 100vh; font-family: Verdana, Geneva, Tahoma, sans-serif; } body, .uploadForm, .uploadForm__label { display: flex; align-items: center; justify-content: center; } .uploadForm { width: 600px; max-width: 600px; height: 300px; flex-direction: column; border: 2px dashed gray; font-family: inherit; } .uploadForm__input { display: none; } .uploadForm__label { border: 1px solid gray; padding: .5em 2em; color: deepskyblue; transition: transform .4s; flex-direction: column; } .uploadForm__label:hover { cursor: pointer; transform: scale(1.01); box-shadow: grey 2px 2px 10px; } .uploadForm__icon { font-size: 1.8em; } .notification { display: none; } .success, .error { right: 30px; z-index: 10; width: 300px; bottom: 40px; padding: 1em; height: auto; text-align: center; display: block; position: fixed; font-family: inherit; animation: alert .8s forwards; } .notification.success { background: #D9EDF7; color: #31709C; } .notification.error { background: #F2DEDE; color: #B24842; } @keyframes alert { 0% { opacity: 0; bottom: -40px; } 100% { opacity: 1; bottom: 40px; } } 

Next let's create the app.js file inside public folder too and write the following code:

(function (d, axios) { "use strict"; var inputFile = d.querySelector("#inputFile"); var divNotification = d.querySelector("#alert"); inputFile.addEventListener("change", addFile); function addFile(e) { var file = e.target.files[0] if(!file){ return } upload(file); } function upload(file) { var formData = new FormData() formData.append("file", file) post("/upload", formData) .then(onResponse) .catch(onResponse); } function onResponse(response) { var className = (response.status !== 400) ? "success" : "error"; divNotification.innerHTML = response.data; divNotification.classList.add(className); setTimeout(function() { divNotification.classList.remove(className); }, 3000); } })(document, axios) 

Finally we need to create api.js file one more time inside public folder and write this code:

"use strict"; function post(url, data) { return axios.post(url, data) .then(function (response) { return response; }).catch(function (error) { return error.response; }); } 

Well, after all this let's finally write some Go code. I'm going to start by creating the file.go file inside controllers folder and write the following:

package controllers import ( "fmt" "io/ioutil" "mime/multipart" "net/http" ) // UploadFile uploads a file to the server func UploadFile(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Redirect(w, r, "/", http.StatusSeeOther) return } file, handle, err := r.FormFile("file") if err != nil { fmt.Fprintf(w, "%v", err) return } defer file.Close() mimeType := handle.Header.Get("Content-Type") switch mimeType { case "image/jpeg": saveFile(w, file, handle) case "image/png": saveFile(w, file, handle) default: jsonResponse(w, http.StatusBadRequest, "The format file is not valid.") } } func saveFile(w http.ResponseWriter, file multipart.File, handle *multipart.FileHeader) { data, err := ioutil.ReadAll(file) if err != nil { fmt.Fprintf(w, "%v", err) return } err = ioutil.WriteFile("./files/"+handle.Filename, data, 0666) if err != nil { fmt.Fprintf(w, "%v", err) return } jsonResponse(w, http.StatusCreated, "File uploaded successfully!.") } func jsonResponse(w http.ResponseWriter, code int, message string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(code) fmt.Fprint(w, message) } 

As our final step let's create the main.go file in your root directory and use the file package like so:

package main import ( "log" "net/http" // Note this is my path according to my GOPATH, chage it according to yours. "bitbucket.org/gustavocd/upload-img/controllers" ) func main() { http.Handle("/", http.FileServer(http.Dir("./public"))) http.HandleFunc("/upload", controllers.UploadFile) log.Println("Running") http.ListenAndServe(":8080", nil) } 

Notes

  1. I'm not handling validation (you should do it), but it should work like a charm.
  2. I'm using my GOPATH root, please change according to yours.
  3. Keep learning and happy code :).

Top comments (3)

Collapse
 
ajinkyax profile image
Ajinkya Borade

why not use io.Copy

Collapse
 
fattahmuhyiddeen profile image
fattahmuhyiddeen

case "image/jpeg":
saveFile(w, file, handle)
case "image/png":
saveFile(w, file, handle)

can be ->

case "image/jpeg", "image/png":
saveFile(w, file, handle)

Collapse
 
ltpyt profile image
ltpyt

@ajinkya how would the routine looks with io.Copy ?