Reading a csv file in Golang function executed in browser

Viewed 126

I have written a script in Golang to sort user stories stored in a csv and create sprints with a fixed team velocity. The following is the part related to reading the csv file.

// userstory.go
f, err := os.Open("userstories.csv")
if err != nil {
    log.Fatal(err)
} the file at the end of the program
defer f.Close()
csvReader := csv.NewReader(f)
data, err := csvReader.ReadAll()

Now, since I aim to make it work in the browser (with no backend) and the golang code is compiled to wasm, I can't use os files anymore.

So I'm trying to get the ArrayBuffer from the File and pass it as an Uint8Array argument (from JavaScript) to the function GetSprints:

// js code
const runScript = async () => {
  let files = data.fileList;    

  files[0].arrayBuffer().then((x) => {
    var b = new Uint8Array(x);
    console.log(b)
    console.log(GetSprints(b));
  })
}

GetSprints has been exposed to JavaScript with the following js.Global().Set:

// userstory.go
func main() {
    fmt.Println("Go WebAssembly: loaded UserStory.go")
    js.Global().Set("GetSprints", getSprintsWrapper())
    <-make(chan bool)
}

and the Uint8Array passed as first argument, is copied to a []byte buffer:

// userstory.go
func getSprintsWrapper() js.Func {
    getSprintsFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
        var dst []byte
        js.CopyBytesToGo(dst, args[0])

        return GetSprints(dst)
    })
    return getSprintsFunc
}

Given that csv.NewReader accepts a io.Reader as argument, i tried to replace the file descriptor with bytes.Reader

// userstory.go
func GetSprints(inputFile []byte) []Sprint {
  f := bytes.NewReader(inputFile)
  csvReader := csv.NewReader(f)
  data, err := csvReader.ReadAll()
  if err != nil {
    log.Fatal(err)
  }

  fmt.Println("No errors reading csv. Data: ", data)
  // ...
}

but it results in an empty array. enter image description here

Someone else interested in parsing files with golang code compiled to wasm?

Thanks!

1 Answers

It worked replacing the arrayBuffer() method with text().

The method text() "returns" (allow me the term but it returns a Promise) a UTF-8 string representing the content of the file.

// js code
const runScript = async () => {
  
  let files = data.fileList;    
  files[0].text().then((x) => {
    console.log(GetSprints(x));
  })
}

Then on the golang side, before passing the string, I converted it to []byte

// userstory.go
func getSprintsWrapper() js.Func {
    getSprintsFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
        fmt.Println([]byte(args[0].String()))
        return GetSprints([]byte(args[0].String()))
    })
    return getSprintsFunc
}

and the GetSprints is finally able to process the csv.

// userstory.go
func GetSprints(inputFile []byte) []Sprint {
    f := bytes.NewReader(inputFile)
    csvReader := csv.NewReader(f)

    fmt.Println(csvReader.Read())
    data, err := csvReader.ReadAll()
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("No errors reading csv. Data: ", data)

enter image description here


Edit: Added a JSON.parse call on the js

// js
files[0].text().then((x) => {
 var sprints = JSON.parse(GetSprints(x));
   console.log(sprints);
})

and json.Marshal on golang side

// userstory.go
func getSprintsWrapper() js.Func {
    getSprintsFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
        b, err := json.Marshal(GetSprints([]byte(args[0].String())))
        if err != nil {
            fmt.Println(err)
        }
        return string(b)
    })
    return getSprintsFunc
}

so now the results can be handled as javascript objects. enter image description here

If anyone has any suggestions to improve it, please let me know. Thank you

Related