Read multipart-form data as []byte in GO - go

I'm using GIN as GO framework, I'm having an issue when uploading file and directly convert image as byte so I can store it in my BLOB field inside db table, so I have my piece of code like this:
func (a *AppHandler) Upload(ctx *gin.Context) {
form := &struct {
Name string `form:"name" validate:"required"`
Token string `form:"token" validate:"required"`
AppCode string `form:"app_code" validate:"required"`
}{}
ctx.Bind(form)
if validationErrors := a.ValidationService.ValidateForm(form); validationErrors != nil {
httpValidationErrorResponse(ctx, validationErrors)
return
}
file, header, err := ctx.Request.FormFile("file")
and I'm trying to store it in db like this
app.SetFile(file)
a.AppStore.Save(app)
and it returns this kind of error:
cannot use file (type multipart.File) as type []byte

*multipart.File implements io.Reader interface so you could copy its content into a bytes.Buffer like this:
file, header, err := ctx.Request.FormFile("file")
defer file.Close()
if err != nil {
return nil, err
}
buf := bytes.NewBuffer(nil)
if _, err := io.Copy(buf, file); err != nil {
return nil, err
}
and then add to your app
app.SetFile(buf.Bytes())

Related

Filter JSON returned from GitHub API via Go

I'm trying to get the latest release information from a github repository and grab a specific asset in that release. The following code prints out the release tag and the all the assets under the Asset structure. I'd like to be able to pull out a specific item and its download url, such as just the ajour.exe tag. Can I do that via the struct, or should I be parsing the output to grab it?
func GetGithubAsset() {
testUri := "https://api.github.com/repos/ajour/ajour/releases/latest"
type githubApiData struct {
AppVersion string `json:"tag_name"`
Assets []struct {
Name string `json:"name"`
BrowserDownloadURL string `json:"browser_download_url"`
}
}
resp, err := http.Get(testUri)
if err != nil {
log.Fatal(err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
var data githubApiData
jsonErr := json.Unmarshal(body, &data)
if jsonErr != nil {
log.Fatal(jsonErr)
}
fmt.Println(data.AppVersion)
fmt.Println(data.Assets)
}
You're almost there. Note that the json.Unmarshal does parse the output for you. All you need to is loop through the Assets field, something like so (in place of fmt.Println(data.Assets)):
for _, asset := range data.Assets {
if asset.Name == "ajour.exe" {
fmt.Println(asset.BrowserDownloadURL)
}
}
The json tag json:"assets" is missing
func GetGithubAsset() {
testUri := "https://api.github.com/repos/ajour/ajour/releases/latest"
type githubApiData struct {
AppVersion string `json:"tag_name"`
Assets []struct {
Name string `json:"name"`
BrowserDownloadURL string `json:"browser_download_url"`
} `json:"assets"`
}
resp, err := http.Get(testUri)
if err != nil {
log.Fatal(err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
var data githubApiData
jsonErr := json.Unmarshal(body, &data)
if jsonErr != nil {
log.Fatal(jsonErr)
}
fmt.Println(data.AppVersion)
fmt.Println(data.Assets)
}

Unable to Unmarshall to types.Struct with Protobuf

I am working on a legacy Go app that it's driving me crazy in terms of type definition.
At some point I need to return a value with the value types.Struct (from github.com/gogo/protobuf/types).
Here is the signature for the function:
GetDeviceConfig(ctx context.Context, in *DeviceID, opts ...grpc.CallOption) (*types.Struct, error)
I'm getting the value from a Postgres database (with GORM) using a string column called "config". It could be any type of JSON.
Just an example:
{"fields":{"A":{"Kind":{"string_value":"B"}},"C":{"Kind":{"string_value":"D"}}}}
When I try to unmarshall to a map[string]interface{} it works like a charm:
err := client.DB.Where("id = ?", id, 0).First(&device).Error
if err != nil && err != gorm.ErrRecordNotFound {
return nil, err
}
var dat map[string]interface{}
if err := json.Unmarshal([]byte(device.Config), &dat); err != nil {
panic(err)
}
but I need to conver it to types.Struct (valid protobuf signature that cannot be changed). My best guess was something like this:
// convert json to struct
s := types.Struct{}
if err := json.Unmarshal([]byte(device.Config), &s); err != nil {
panic(err)
}
// fmt.Println(s)
but s is empty. No data is populated.
Any help?
In order to unmarshal JSON into a Struct, you need to use Unmarshaler found in the jsonpb package. Something along the following lines should work:
err := jsonpb.UnmarshalString(device.Config, &s)
[...]
You can find further examples in jsonpb_test.go.

Bigquery Entity Tag

When I loaded my data from Cloud Storage into Bigquery, the click_url field turned into Record type when get created even though it's a string (maybe because of the noindex not sure tho). When I try to insert the data into BigQuery using Inserter. I got this error message:
Cannot convert std::string to a record field:optional .Msg_0_CLOUD_QUERY_TABLE.Msg_1_CLOUD_QUERY_TABLE_click_url click_url = 1
Table in bigquery:
Schema:
Here's the code:
type Product struct {
Name string `datastore:"name" bigquery:"name"`
ClickUrl string `datastore:"click_url,noindex" bigquery:"click_url"`
DateAdded time.Time `datastore:"date_added" bigquery:"date_added"`
}
func insertRows(data interface{}) error {
projectID := "my-project-id"
datasetID := "mydataset"
tableID := "mytable"
ctx := context.Background()
client, err := bigquery.NewClient(ctx, projectID)
if err != nil {
return fmt.Errorf("bigquery.NewClient: %v", err)
}
defer client.Close()
inserter := client.Dataset(datasetID).Table(tableID).Inserter()
if err := inserter.Put(ctx, data); err != nil {
return err
}
return nil
}
func main() {
product := Product{"product_name", "click_url", "date_added_value"} // Example data from datastore
if err := insertRows(product); err != nil {
fmt.Println(err)
}
}
What should I put on the entity tag "bigquery:click_url" to make this work?
Because the ClickUrl in BigQuery is a STRUCT type, which has key-value pairs.
Maybe try this?
type Product struct {
Name string `datastore:"name" bigquery:"name"`
ClickUrl struct {
String string
Text string
Provided string
} `datastore:"click_url,noindex" bigquery:"click_url"`
DateAdded time.Time `datastore:"date_added" bigquery:"date_added"`
}

csv writer using buffer results in empty output

I have created a function to get some data and write them to CSV and the output is stored on a buffer.
type OptIn struct {
Email string `json:"email"`
LastUpdate string `json:"opt_in_last_update"`
}
func writeCSV(data []OptIn) ([]byte, error) {
var buf bytes.Buffer
writer := csv.NewWriter(&buf)
defer writer.Flush()
for _, obj := range data {
var record []string
record = append(record, obj.Email)
record = append(record, obj.LastUpdate)
err := writer.Write(record)
if err != nil {
panic(err.Error())
}
}
return buf.Bytes(), nil
}
The problem is that the buf.Bytes() is always empty, even though the input is not empty and there are no errors thrown.
You need to call writer.Flush() before calling .Bytes() and check .Error() before returning:
// TODO: remove `defer writer.Flush()`
// ...
writer.Flush()
if err := writer.Error(); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
Your sample code does things in the following order:
call buf.Bytes()
return the generated byte slice and nil
call writer.Flush()
Clearly this is not the intended order since we need to flush (and check for any writer errors!) before accessing the generated byte slice.

Generic function to store Go Encoded Data

I need to write a generic function which can store objects as gobjects.
func hash_store(data map[string]string) {
//initialize a *bytes.Buffer
m := new(bytes.Buffer)
//the *bytes.Buffer satisfies the io.Writer interface and can
//be used in gob.NewEncoder()
enc := gob.NewEncoder(m)
//gob.Encoder has method Encode that accepts data items as parameter
enc.Encode(data)
//the bytes.Buffer type has method Bytes() that returns type []byte,
//and can be used as a parameter in ioutil.WriteFile()
err := ioutil.WriteFile("dep_data", m.Bytes(), 0600)
if err != nil {
panic(err)
}
fmt.Printf("just saved all depinfo with %v\n", data)
n,err := ioutil.ReadFile("dep_data")
if err != nil {
fmt.Printf("cannot read file")
panic(err)
}
//create a bytes.Buffer type with n, type []byte
p := bytes.NewBuffer(n)
//bytes.Buffer satisfies the interface for io.Writer and can be used
//in gob.NewDecoder()
dec := gob.NewDecoder(p)
//make a map reference type that we'll populate with the decoded gob
//e := make(map[int]string)
e := make(map[string]string)
//we must decode into a pointer, so we'll take the address of e
err = dec.Decode(&e)
if err != nil {
fmt.Printf("cannot decode")
panic(err)
}
fmt.Println("after reading dep_data printing ",e)
}
In this function I know the data type to be stored in map[string]string . But I need to write a generic function where I don't know data type and still store it as a gobject in a file.
Change your concrete type (map[string]string) to the empty interface type (interface{}).
See this related question why this works.
Encoding:
func store(data interface{}) {
m := new(bytes.Buffer)
enc := gob.NewEncoder(m)
err := enc.Encode(data)
if err != nil { panic(err) }
err = ioutil.WriteFile("dep_data", m.Bytes(), 0600)
if err != nil { panic(err) }
}
Decoding:
func load(e interface{}) {
n,err := ioutil.ReadFile("dep_data")
if err != nil { panic(err) }
p := bytes.NewBuffer(n)
dec := gob.NewDecoder(p)
err = dec.Decode(e)
if err != nil { panic(err) }
}
The value you put in load must be a pointer of the type you stored in the file using gob.
Example for map[string]string:
org := map[string]string{"foo": "bar"}
store(org)
var loadedMap map[string]string
load(&loadedMap)
fmt.Println(loadedMap["foo"]) // bar
When you encode the data, give the Encoder a *interface{}, then you can decoded with a *interface{}
var to_enc interface{} = ...;
god.NewEncoder(...).Encode(&to_enc);
...
var to_dec interface{}
god.NewDecoder(...).Decode(&to_dec);

Resources