3

I am new to Golang and now I'm writing small app which parse some page. I got index page:

<form action="/" method='POST'>
    Enter credentials:<br>
    Login: <input type="text" name="login" >
    <br>
    Password: <input type="password" name="password" >
    <br>
    Search link:<br>
    <input type="text" name="link" >
    <br>
    <input type="submit" value="Submit">
 </form> 

So after clicking Submit button it goes to parse page (using tebeca/selenium pkg), and in the end writes data to result.csv like

func writeToCSV(users []linkedinUser) {
data := users
enc := struct2csv.New()
rows, err := enc.Marshal(data)
if err != nil {
    fmt.Printf("ERROR marshaling file, %s", err)
}
file, err := os.Create("result.csv")
if err != nil {
    fmt.Printf("ERROR creating file, %s", err)
}
defer file.Close()

writer := csv.NewWriter(file)
defer writer.Flush()

for _, value := range rows {
    err := writer.Write(value)
    if err != nil {
        fmt.Printf("ERROR writing file, %s", err)
    }
}

And in the end I got result.csv file in /home/username/go/src/parser/parsertool/src/result.csv

my main.go

func main() {
    r := mux.NewRouter()

    r.HandleFunc("/", indexHandler)
    http.Handle("/", r)

    err := http.ListenAndServe(":3000", nil) // setting listening port
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }


    func indexHandler(w http.ResponseWriter, r *http.Request) {
    t, _ := template.ParseFiles("./front/index.html")
    badLoginT, _ := template.ParseFiles("./front/error.html")
    r.ParseForm()
    login := r.PostFormValue("login")
    password := r.PostFormValue("password")
    link := r.PostFormValue("link")
    if login != "" || password != "" {
        userCred := logincredentials.SetCredentials(login, password)
        if logincredentials.CheckCredentials(userCred) && link != "" {
            crawler.Crawl(link, login, password)
        } else {
            badLoginT.Execute(w, nil)
        }
    }

    t.Execute(w, nil)
}

So the main question: is there a way to get a link to download result.csv to my hard drive? The implementation isn`t important. May be in the end of writeToCSV() when result.csv is ready, window where you could choose where to save file will appear, or after getting result.csv, some link like "Click to download" will appear and after clicking you can see download window etc.

I'm stuck and really don't have any idea how to implement downloading. I would be grateful for any help.

edkeveked
  • 17,989
  • 10
  • 55
  • 93
WannaBeBetter
  • 81
  • 2
  • 13

2 Answers2

4

On your Go server

func main() {
  r := mux.NewRouter()
  r.HandleFunc("/", indexHandler)
  http.Handle("/", r)
  err := http.ListenAndServe(":3000", nil) // setting listening port
  if err != nil {
    log.Fatal("ListenAndServe: ", err)
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
    ...
    Openfile, err := os.Open(Filename)
    FileSize := strconv.FormatInt(FileStat.Size(), 10)
    FileContentType := http.DetectContentType(FileHeader)
    //Send the headers before sending the file
    writer.Header().Set("Content-Disposition", "attachment; filename="+Filename)
    writer.Header().Set("Content-Type", FileContentType)
    writer.Header().Set("Content-Length", FileSize)

    //Send the file
   io.Copy(w, Openfile) 
  }

In your client

Since the file to be downloaded will be sent by the server, one need to use a js function to post the form first to the server. Then after receiving the response of the server, the download will be triggered directly by the same function.

html

<form>
    Enter credentials:<br>
    Login: <input id="login" type="text" name="login" >
    <br>
    Password: <input id="password" type="password" name="password" >
    <br>
    Search link:<br>
    <input id="link" type="text" name="link" >
    <br>
    <input type="button" onclick="downloadFile()">
 </form> 

In a js file

function downloadFile() {
      var data = { 
       login: document.querySelector('#login').value,
       password: document.querySelector('#password').value,
       link: document.querySelector('#link').value
       }

      xhttp = new XMLHttpRequest();
      xhttp.onreadystatechange = function() {
        var a;
        if (xhttp.readyState === 4 && xhttp.status === 200) {
            a = document.createElement('a');
            a.href = window.URL.createObjectURL(xhttp.response);
            // Give to file the name you wish 
            a.download = "test-file.xls";
            a.style.display = 'none';
            document.body.appendChild(a);
            a.click();
        }
    }
    }

    xhttp.open("POST", urlserver);
    xhttp.setRequestHeader("Content-Type", "application/json");
    // set responseType as blob for binary responses
    xhttp.responseType = 'blob';
    xhttp.send(JSON.stringify(data));
edkeveked
  • 17,989
  • 10
  • 55
  • 93
1

Golang makes serving files easy. You can use the FileServer component in net/http (see below). You just need to make sure to save the file in the folder that you are serving ("/static" in the example below). So your code should use a dedicated path:

file, err := os.Create("/home/username/static/result.csv")

And with the following FileServer handler added, your file would be available under http://domain/static/result.csv.

package main

import (
    "net/http"
)

func main() {
...
    fs := http.FileServer(http.Dir("/home/username/static"))
    http.Handle("/static/", http.StripPrefix("/static", fs))
    http.ListenAndServe(":3000", nil)
...
}

Note that all files under static will be served. For more information see the docs.

If you need help how to write the client side for a file download please refer to this stackoverflow question.

Oswin Noetzelmann
  • 9,166
  • 1
  • 33
  • 46