Stanley Stanley - 2 months ago 6
HTTP Question

Forwarding a file upload

I'm working on an api endpoint in go that will accept an upload and then immediately forward to another API. I don't want to write the file to disk anywhere, but I'm not sure storing the file temporarily in memory the way I have is correct either. All the examples that I can find deal with saving the file to disk. I've posted what I'm doing below. The response I get back from the second API is that I failed to post a file, but I can see that it is receiving the "userID" field. Can someone please point out what I'm doing wrong as well as possibly advise if this is the best way to go about this?

Route Handler

func (r *Routes) forwardFile(w http.ResponseWriter, req *http.Request){
parameters := mux.Vars(req)
userID := parameters["userID"]

const maxFileSize = 1 * 1024 * 1024 // 1MB

// pull in the uploaded file into memory
req.ParseMultipartForm(maxFileSize)


file, fileHeader, err := req.FormFile("fileUpload")
if err != nil {
encodeResponse(w, req, response{obj: nil, err: err})
return
}
defer file.Close()

success, err := service.DoForwardFile(userID, file, fileHeader)
encodeResponse(w, req, response{obj: success, err: err})
}


Service Handler

func (b *base) DoForwardFile(userID int, file multipart.File, fileHeader *multipart.FileHeader) (FileForwardedResponse, error) {
// start building our request to forward the file
var resp *http.Response
defer func() {
if resp != nil {
resp.Body.Close()
}
reportStat.Complete(0)
}()

// build a form body
body := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(body)

// add form fields
bodyWriter.WriteField("userID", userID)

// add a form file to the body
fileWriter, err := bodyWriter.CreateFormFile("fileUpload", fileHeader.Filename)
if err != nil {
return FileForwardedResponse{}, err
}
// copy the file into the fileWriter
_, err = io.Copy(fileWriter, file)
if err != nil {
return FileForwardedResponse{}, err
}

// Close the body writer
bodyWriter.Close()

// build request url
apiURL := fmt.Sprintf("%s/v2/users/%d/files", config.APIURL, userID)

// send request
client := &http.Client{Timeout: time.Second * 10}
req, err := http.NewRequest("POST", apiURL, body)
resp, err = client.Do(req)

...


}

Answer

You're not setting the Content-Type for the request. Even if the header gets set automatically to multipart/form-data, it's missing the data boundary.

req, err := http.NewRequest("POST", uri, body)
if err != nil {
    return FileForwardedResponse{}, err
}
req.Header.Set("Content-Type", bodyWriter.FormDataContentType())
...
Comments