Verified Solution[StackOverflow/go] golang POST image and text field with multipart/form-data
Sponsored Content
### ROOT CAUSE
The issue arises when attempting to send a multipart/form-data request in Go with both text and image data. The root cause is the lack of proper handling of multipart form data creation, specifically:
1. **Missing multipart.Writer**: Without creating a multipart.Writer, the request cannot properly structure binary and text parts.
2. **Incorrect Content-Type**: The Content-Type header is not set to the correct multipart/form-data value with the appropriate boundary.
3. **File Handling**: The image file is not being read and attached as a form part, leading to missing or corrupted data.
### CODE FIX
```go
package main
import (
"bytes"
"fmt"
"mime/multipart"
"net/http"
"os"
"io"
)
func uploadHandler(w http.ResponseWriter, r *http.Request) {
// Create multipart writer
body := &bytes.Buffer{}
wr := multipart.NewWriter(body)
// Add text field
part, err := wr.CreateFormFile("text_field", "data.txt")
if err != nil {
http.Error(w, "Error creating text part", http.StatusInternalServerError)
return
}
part.Write([]byte("Sample text content"))
if err := part.Close(); err != nil {
http.Error(w, "Error writing text part", http.StatusInternalServerError)
return
}
// Add image file
file, header := r.FormFile("image_field")
defer file.Close()
part, err = wr.CreateFormFile("image_field", header.Filename)
if err != nil {
http.Error(w, "Error creating image part", http.StatusInternalServerError)
return
}
if _, err := io.Copy(part, file); err != nil {
http.Error(w, "Error reading image file", http.StatusInternalServerError)
return
}
if err := part.Close(); err != nil {
http.Error(w, "Error closing image part", http.StatusInternalServerError)
return
}
// Close multipart writer and set headers
if err := wr.Close(); err != nil {
http.Error(w, "Error closing multipart writer", http.StatusInternalServerError)
return
}
// Send response with image URL
url := fmt.Sprintf("http://example.com/uploads/%s", header.Filename)
fmt.Fprintf(w, "Upload successful! Image URL: %s", url)
// Handle file saving (example using os.Create)
saveFile, err := os.Create(fmt.Sprintf("/path/to/save/%s", header.Filename))
if err != nil {
http.Error(w, "Error saving file", http.StatusInternalServerError)
return
}
defer saveFile.Close()
io.Copy(saveFile, file)
}
```
**Client-Side Example (POST Request)**:
```go
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile("text_field", "data.txt")
if err := part.Write([]byte("Hello, World!")); err != nil { /* handle error */ }
if _, err := writer.CreateFormFile("image_field", "image.jpg"); err != nil { /* handle error */ }
writer.Close()
req, err := http.NewRequest("POST", "http://example.com/upload", body)
if err != nil { /* handle error */ }
req.Header.Set("Content-Type", writer.FormDataContentType())
// Send request with http.Client
```
**Key Fixes**:
1. **multipart.Writer**: Properly structures form data with boundaries.
2. **File Handling**: Uses `r.FormFile` to read uploaded files and copies them to the form part.
3. **Content-Type**: Automatically generates the correct boundary with `w.FormDataContentType()`.
4. **Error Handling**: Checks each step to handle potential failures.
Deploy on DigitalOcean ($200 Credit)
Related Fixes
[StackOverflow/reactjs] change Emmet abbreviation ".." from 'styleName' to 'className'
[StackOverflow/rust] How do I make asynchronous parallel requests in a separate thread?
[rust-lang/rust] [ICE]: `type variables should not be hashed`