'How to parse images from FormData typescript frontend to golang backend using gorilla websocket?

Summary

Typescript Frontend:

I defined a ´formData´ constant which is appending a collection of images on submit. Code fragment is shown below:

onSubmit={async ({ title, price, category, state, description, availability, brand, labels, sku, images }) => {
    console.log("Submitting")
    const formData = new FormData();
    Object.values(images).map((val:any) => {
        formData.append("myImage", val, val.name);
    })
    const d = { title, price, category, state, description, availability, brand, labels, sku, formData };
    const resp = await conn.mutation.createMarketplacePublication(d);
}}
createMarketplacePublication: (data: {
    title: string;
    price: number;
    category: string;
    state: string;
    description: string;
    availability: string;
    brand: string;
    labels: string;
    sku: string;
    formData: FormData; //Also tried with Object and Blob types
}): Promise<{ error: string } | { room: Room }> =>
    connection.fetch("create_marketplace_publication", data)

Golang Backend:

For testing purposes, I'm just trying to print the ´formData´ variable to check how it is structured, but it shows an empty map formData:map[] which according to docs is just an empty slice. Relevant code fragment flow are shown below (gorilla websocket package is being used)

Client:

package websocket

import (
    "fmt"
    "runtime/debug"

    "github.com/gorilla/websocket"
)

...
type Client struct {
    Addr          string          
    Socket        *websocket.Conn 
    Send          chan []byte     
    AppId         uint32          
    UserId        string          
    FirstTime     uint64          
    HeartbeatTime uint64          
    LoginTime     uint64          
}

func NewClient(addr string, socket *websocket.Conn, firstTime uint64) (client *Client) {
    client = &Client{
        Addr:          addr,
        Socket:        socket,
        Send:          make(chan []byte, 100),
        FirstTime:     firstTime,
        HeartbeatTime: firstTime,
    }

    return
}
...
func (c *Client) Read() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("write stop", string(debug.Stack()), r)
        }
    }()

    defer func() {
        fmt.Println("send", c)
        close(c.Send)
    }()

    for {
        _, message, err := c.Socket.ReadMessage()
        if err != nil {
            fmt.Println("message error", c.Addr, err)

            return
        }
        ProcessData2(c, message)
    }
}

func (c *Client) Write() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("write stop", string(debug.Stack()), r)

        }
    }()

    for {
        select {
        case message, ok := <-c.Send:
            if !ok {
                fmt.Println("Client", c.Addr, "ok", ok)

                return
            }

            c.Socket.WriteMessage(websocket.TextMessage, message)
        }
    }
}

func (c *Client) SendMsg(msg []byte) {

    if c == nil {

        return
    }

    defer func() {
        if r := recover(); r != nil {
            fmt.Println("SendMsg stop:", r, string(debug.Stack()))
        }
    }()

    c.Send <- msg
}

func (c *Client) close() {
    close(c.Send)
}
...

Process:

package websocket
import (
    "encoding/json"
    "fmt"
    "log"
    "sync"

    "../common"

    "../models"
)

type DisposeFunc func(client *Client, seq string, icmd string, message []byte) (code uint32, msg string, ocmd string, data interface{}, voice bool)

var (
    handlers        = make(map[string]DisposeFunc)
    handlersRWMutex sync.RWMutex
)

// 注册
func Register(key string, value DisposeFunc) {
    handlersRWMutex.Lock()
    defer handlersRWMutex.Unlock()
    handlers[key] = value

    return
}

func getHandlers(key string) (value DisposeFunc, ok bool) {
    handlersRWMutex.RLock()
    defer handlersRWMutex.RUnlock()

    value, ok = handlers[key]

    return
}

func ProcessData(client *Client, message []byte) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Procesamiento de datos stop ->", r)
        }
    }()

    request := &models.Request{}
    if string(message) == "ping" {
        _ = client.Socket.WriteJSON("pong")
        log.Printf("Message sent: %s", "pong")
        return
    }
    err := json.Unmarshal(message, request)
    if err != nil {
        fmt.Println("Error en Procesamiento de datos json Unmarshal(message, request)", err)
        client.SendMsg([]byte("数据不合法"))

        return
    }
    requestData, err := json.Marshal(request.Data)
    if err != nil {
        fmt.Println("Error en Procesamiento de datos json Marshal(request.Data)", err)
        client.SendMsg([]byte("No se pudieron procesar los datos"))

        return
    }

    seq := request.Seq
    cmd := request.Cmd
    // ref := request.Ref
    fetchId := request.FetchId

    var (
        code  uint32
        msg   string
        data  interface{}
        voice bool
    )

    if value, ok := getHandlers(cmd); ok {
        code, msg, cmd, data, voice = value(client, seq, cmd, requestData)
    } else {
        code = common.RoutingNotExist
        fmt.Println("El enrutamiento de datos de procesamiento no existe", client.Addr, "cmd", cmd)
    }

    msg = common.GetErrorMessage(code, msg)
    responseHead := models.NewResponseHead(seq, cmd, fetchId, code, msg, data)

    headByte, err := json.Marshal(responseHead)
    if err != nil {
        fmt.Println("error json Marshal", err)

        return
    }

    client.SendMsg(headByte)
    if voice == true {
        msg = common.GetErrorMessage(code, msg)
        responseHead := models.NewResponseHead(seq, cmd, fetchId, code, msg, data)

        headByte, err := json.Marshal(responseHead)
        if err != nil {
            fmt.Println("error json Marshal", err)

            return
        }
        client.SendMsg(headByte)
    }
    return
}

Routes:

package routers

import (
    "../websocket"
)

// Websocket
func WebsocketInit() {
    ...
    websocket.Register("create_marketplace_publication", websocket.CreateMarketplacePublicationController)
}

Controller:

package websocket

import (
    "encoding/json"
    "fmt"
    "path/filepath"
    "time"

    "../../database"
    "../common"
    "../models"
    "../repository/crud"
    "../utils/console"
)
...
go
func CreateMarketplacePublicationController(client *Client, seq string, icmd string, message []byte) (code uint32, msg string, cmd string, data interface{}, voice bool) {
    request := map[string]interface{}{
        "title":        "",
        "price":        "",
        "category":     "",
        "state":        "",
        "description":  "",
        "availability": "",
        "brand":        "",
        "labels":       "",
        "sku":          "",
        "formData":     "",
    }
    if err := json.Unmarshal(message, &request); err != nil {
        code = common.ParameterIllegal
        return
    }
    form := request["formData"]
    //p, err := ioutil.ReadFile(form)
    //if err != nil {
    //  fmt.Println("Error on reading file", err)
    //}
    fmt.Println("post publication content: ", request["formData"])
    ...
}

Issue: I can't use ioutil package due to the Type assertion issue: cannot use form (type interface {}) as type string in argument to ioutil.ReadFile: need type assertion. I already checked similar questions and answers from this forum and other website regarding passing file data to server and handling multipart data in golang but couldn't find any solution.

Tried this:

fmt.Println("check request: ", request["formData"])
md, _ := request["formData"].(map[string]interface{})
fmt.Println(md)
if md == nil {
    fmt.Println("empty")
}
fmt.Println(len(md))

Got this:

check request:  map[]
map[]
0

If I change the FormData type to either Object or Blob, I get this:

check request:  map[blob:http://localhost:3000/33afc417-6577-43d9-ac35-6f5ac626c7ea:map[]]
1

What should I do in this particular case? Similar cases use the httprequest.MultiformData function not websockets.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source