gin-gonic / gin

Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.
https://gin-gonic.com/
MIT License
78.8k stars 8.02k forks source link

Sending a multipart.File to another micro-service #1357

Closed adriendomoison closed 6 years ago

adriendomoison commented 6 years ago

I am retrieving a file from a multipart/form-data upload with file, header, err := c.Request.FormFile("file") and after some processing I need to send the file to another micro-service. I was trying to use a classic POST and BIND with a structure like that


type RequestDTOWithFile struct {
    File        *multipart.File `json:"file"`
    PublicId    string         `json:"order_id"`
    Name        string         `json:"name"`
    UserId      uint           `json:"user_id"`
}

// send a RequestDTOWithFile from first micro service
http.NewRequest("POST", url, data) 

...
// recieve a RequestDTOWithFile from an other micro service
c.Bind(&data)

But I got stuck with json: cannot unmarshal object into Go struct field RequestDTOWithFile.file of type multipart.File

Is there a way to achieve that with gin?

For now, the only solution I found that could work is to recreate a multipart form like this https://stackoverflow.com/questions/20205796/golang-post-data-using-the-content-type-multipart-form-data/20397167 which is not elegant.

delphinus commented 6 years ago

You cannot unmarshal JSON into such struct because multipart.File is an interface, not a struct type. Go can't know the real struct type from pure JSON string.

If you want to achieve that, you should use a way that can store Go's type structures in addition to its data. You can use gob for such cases.

https://gist.github.com/delphinus/e36cd3dfd00eb8e5a0e87456fc9809e2

I wrote an example for this.

$ curl 0:8080/json
{"foobar":{"barbar":"bar string"}}
# this fails because Go cannot detect types from JSON string only.
$ curl 0:8080/json-post -X POST -d '{"foobar":{"barbar":"bar string"}}'
{"err":"json: cannot unmarshal object into Go struct field foo.foobar of type main.foobar"}%

# gob is a binary encoding that can store Go's type structures.
$ curl 0:8080/gob > gob.dat
$ xxd gob.dat
00000000: 1cff 8103 0101 0366 6f6f 01ff 8200 0101  .......foo......
00000010: 0106 466f 6f42 6172 0110 0000 002f ff82  ..FooBar...../..
00000020: 010c 2a6d 6169 6e2e 666f 6f66 6f6f ff83  ..*main.foofoo..
00000030: 0301 0106 666f 6f66 6f6f 01ff 8400 0101  ....foofoo......
00000040: 0106 4261 7242 6172 010c 0000 0011 ff84  ..BarBar........
00000050: 0d01 0a62 6172 2073 7472 696e 6700 00    ...bar string..

# Go can successfully detect the real types!
$ curl 0:8080/gob-post -X POST -F 'file=@gob.dat'
{"foobar":{"barbar":"bar string"}}

gob is the simplest way for this. You can choose protobuf for more complex situation (when you use different languages in micro services).

adriendomoison commented 6 years ago

Very graceful! Thanks!

chainhelen commented 6 years ago

@adriendomoison maybe you close this issues?