I am trying to send a file and json data from a client to the server, but the server does not respond to the request and fails to decode the json data but the file is received
I am using a map and I send it as a json format(avsUpload), the reason for this is that the client can have a quantity of data n and a struc is not ideal
client code:
func UploadFile(file_up string,avsUpload map[string]string){
//get file
file, err := os.Open(file_up)
if err!=nil{
fmt.Println(red(" ERROR ")+"open file",file,err)
return
}
defer file.Close()
//get file info
fileInfo, err := os.Stat(file_up)
if err!=nil{
fmt.Println(red(" ERROR ")+"geting file info",err)
return
}
//create form
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile("file", fileInfo.Name())
if err != nil {
fmt.Println(red(" ERROR ")+"creating form file",err)
return
}
io.Copy(part, file)
writer.Close()
//encode json, avsUpload as map
json.NewEncoder(body).Encode(avsUpload)
//create request
request, err := http.NewRequest("POST", "http://127.0.0.1:2047/ctrl/upload", body)
if err != nil {
fmt.Println(red(" ERROR "),err)
return
}
//add headers
request.Header.Add("Content-Type", writer.FormDataContentType())
request.Header.Add("Content-Type","application/json; charset=utf-8")
request.Header.Add("Authorization", "BEARER "+readKey())
request.Header.Add("Content-Length", strconv.FormatInt(request.ContentLength,10))
//create req
client := &http.Client{}
response, err := client.Do(request)
if err != nil {
fmt.Println(red(" ERROR "),err)
return
}
defer response.Body.Close()
content, err := ioutil.ReadAll(response.Body)
if err != nil {
fmt.Println(red(" ERROR "),err)
}
fmt.Println(" "+string(content))
}
capturing the requests, I could see that json data is sent normally
server code:
func Upload(w http.ResponseWriter, r *http.Request){
//set header
w.Header().Set("Content-Type", "multipart/form-data")
w.Header().Set("Content-Type", "application/json")
//set max request size
r.Body = http.MaxBytesReader(w, r.Body, MaxFileSize)
fmt.Println("size>>> ",r.ContentLength)
//close conection if request is > MaxFileSize
if r.ContentLength > MaxFileSize {
http.Error(w, "File size is too large, max "+strconv.Itoa(FileSize)+" mb's
", http.StatusExpectationFailed)
log.Error(w, "File size is too large, max "+strconv.Itoa(FileSize)+" mb's", http.StatusExpectationFailed)
return
}
//create miltipart reader
reader, err := r.MultipartReader()
if err != nil {
log.Error(w, err.Error(), http.StatusBadRequest)
return
}
// parse file form
p, err := reader.NextPart()
if err != nil && err != io.EOF {
log.Error(w, err.Error(), http.StatusInternalServerError)
return
}
//check if te variable file exist in form
if p.FormName() != "file" {
http.Error(w, "file is expected
", http.StatusBadRequest)
log.Error(w, "file is expected", http.StatusBadRequest)
return
}
//check file name length
if len(p.FileName()) > 100 {
http.Error(w, "file name is too long
", http.StatusBadRequest)
log.Error(w, "file name is too long", http.StatusBadRequest)
return
}
//check if te filename contains spaces
var fileName string
if strings.Contains(p.FileName(), " "){
fileName=strings.Replace(p.FileName(), " ", "_", -1)
}else{
fileName=p.FileName()
}
//get user from id in token
_, claims, err := jwtauth.FromContext(r.Context())
if err != nil {
log.Error(w, err.Error(), http.StatusBadRequest)
return
}
user:=getUser(int(claims["id"].(float64)))
//create buffer
buf := bufio.NewReader(p)
//upload file to user dir
f, err := os.OpenFile("test/"+user+"/tmpfile/"+fileName, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
log.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer f.Close()
//decode json from client
avsSelect:=make(map[string]string)
err = json.NewDecoder(r.Body).Decode(&avsSelect)
if err != nil {
log.Error(w, err.Error(), http.StatusBadRequest)
return
}
//copy file to user dir
lmt := io.MultiReader(buf, io.LimitReader(p, MaxFileSize - 511))
fileSize, err := io.Copy(f, lmt)
if err != nil && err != io.EOF {
http.Error(w, "File size is too large, max "+strconv.Itoa(FileSize)+" mb's
", http.StatusExpectationFailed)
log.Error(w, err.Error(), http.StatusInternalServerError)
os.Remove(f.Name())
return
}
defer p.Close()
//print conformation message
w.Write([]byte(fmt.Sprintf(green("SERVER: ")+"File "+fileName+" uploaded")))
fmt.Sprintf("File "+fileName+" uploaded")
log.Info("Size request: %#v
", r.ContentLength)
log.Info("Size file uploaded: %#v
",fileSize)
return
}
The server records the following for diferents request:
invalid character ' ' in literal false (expecting 'a')400
invalid character '¥' looking for beginning of value400"
invalid character '\\u0086' looking for beginning of value400"
It could be an issue with your JWT auth since you're ignoring that potential error coming from jwtauth.FromContext
. Try adding error handling there and see if you get anything useful.
Your multipart construction seems wrong. You create the first part from a file, but you don't create a second part for the json, you decode it directly into to the buffer. Instead, you should use writer.CreatePart
to create a new part, and write the json data to the writer returned from that.