4

Specifically, how to connect <input type="file"> with this function in Go? I know there is "syscall/js" package, but I didn't find any examples with file reading.

func parseCSVFile(filePath string) []LabelWithFeatures {
    fileContent, _ := ioutil.ReadFile(filePath)
    lines := bytes.Split(fileContent, newline)
    numRows := len(lines)

    labelsWithFeatures := make([]LabelWithFeatures, numRows-2)

    for i, line := range lines {
        // skip headers
        if i == 0 || i == numRows-1 {
            continue
        }
        labelsWithFeatures[i-1] = NewLabelWithFeatures(bytes.Split(line, comma))
    }
    return labelsWithFeatures
}
stsdc
  • 406
  • 5
  • 14
  • You can't interact with the local filesystem within a browser for security reasons. – Adrian Jan 21 '19 at 14:24
  • Maybe you need to call `FileReader` JavaScript API ( https://developer.mozilla.org/en-US/docs/Web/API/File/Using_files_from_web_applications ) using `syscall/js` https://blog.gopheracademy.com/advent-2018/go-in-the-browser/ – zakki Jan 22 '19 at 08:21
  • @Adrian You sure can, you just have to ask the user to give you the file. There's APIs as well as just an upload input. – donatJ Aug 08 '20 at 00:59
  • @donatJ that's not the same thing as interacting with the local filesystem. You can't open an arbitrary file path from JavaScript or examine a directory. The only way to access a file is if the user provides it to a file form field. – Adrian Aug 10 '20 at 12:35

2 Answers2

5

I've wanted a satisfactory answer for this for years, finally figured it out the other night.

You can essentially boil the whole thing down to:

    fileInput := document.Call("getElementById", "fileInput")

    fileInput.Set("oninput", js.FuncOf(func(v js.Value, x []js.Value) any {
        fileInput.Get("files").Call("item", 0).Call("arrayBuffer").Call("then", js.FuncOf(func(v js.Value, x []js.Value) any {
            data := js.Global().Get("Uint8Array").New(x[0])
            dst := make([]byte, data.Get("length").Int())
            js.CopyBytesToGo(dst, data)
            // the data from the file is in dst - do what you want with it
            

            return nil
        }))

        return nil
    }))

I wrote a little blog post about it here with the working WASM code running at the bottom

https://donatstudios.com/Read-User-Files-With-Go-WASM

donatJ
  • 3,105
  • 3
  • 32
  • 51
1

You can't really access the filesystem in the browser. wasm_exec.js is used to execute Go webassembly in the browser, it mocks out some filesystem functionality, but I don't think it's very useful to you: https://github.com/golang/go/blob/9d23975d/misc/wasm/wasm_exec.js#L41-L73

The file read method even returns an error by default.

You mentioned <input type="file">. You can get bytes from an uploaded file: Getting byte array through input type = file. You could then pass those bytes to the Golang wasm runtime.

Define a global syscall/js callback in your Go code and call it from the browser to pass the bytes down to the Go runtime.

I would look for blogposts on how to define callbacks from within the Go runtime. Also look out for changes between go 1.11 and 1.12, the api has breaking changes.

maxm
  • 3,412
  • 1
  • 19
  • 27