I created an example to demonstrate how it should work. First, let me present the code, then, I'll walk you through all of the relevant parts.
Upload
The upload is done in the handlers_test.go
file. I wrote two tests to show off how to create a valid HTTP Request with a multipart body (I assumed that the communication was based on HTTP). Here you can find the code:
package multipart
import (
"bytes"
"io"
"mime/multipart"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
func TestHandleFile(t *testing.T) {
t.Run("MultipartRequest", func(t *testing.T) {
// instantiate multipart request
var buf bytes.Buffer
multipartWriter := multipart.NewWriter(&buf)
defer multipartWriter.Close()
// add form field
filePart, _ := multipartWriter.CreateFormFile("file", "file.txt")
filePart.Write([]byte("Hello, World!"))
r := httptest.NewRequest(http.MethodPost, "/file", &buf)
w := httptest.NewRecorder()
r.Header.Set("Content-Type", multipartWriter.FormDataContentType())
HandleFile(w, r)
data, _ := io.ReadAll(w.Result().Body)
assert.Equal(t, http.StatusOK, w.Result().StatusCode)
assert.Equal(t, []byte("Hello, World!"), data)
})
t.Run("PlainRequest", func(t *testing.T) {
r := httptest.NewRequest(http.MethodPost, "/file", nil)
w := httptest.NewRecorder()
HandleFile(w, r)
assert.Equal(t, http.StatusBadRequest, w.Result().StatusCode)
})
}
We can focus on the subtest MultipartRequest
. First, it instantiates a multipart body which will be used later as the request payload of the HTTP request we're going to send. Then, we create a file part and write dummy content to it. Before sending out the request, we've to see the Content-Type
header that will be used for parsing stuff. The rest of the test should be pretty straightforward.
Read
The read (or parsing) is done by the HTTP server. The file involved is the handlers.go
file:
package multipart
import (
"io"
"mime"
"mime/multipart"
"net/http"
"strings"
)
func HandleFile(w http.ResponseWriter, r *http.Request) {
mediaType, params, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
if strings.HasPrefix(mediaType, "multipart/") {
multipartReader := multipart.NewReader(r.Body, params["boundary"])
filePart, _ := multipartReader.NextPart()
// pay attention here when you read large file
data, _ := io.ReadAll(filePart)
w.Write(data)
return
}
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("request is not multipart"))
}
Here, the relevant steps can be summarized in the following list:
- We retrieve and parse the
Content-Type
header from the HTTP Request
- We check if the above value starts with the string
multipart/
- If so, we read the next (and only) part of the body and we write its content to the response stream
- If not, we return a
BadRequest
error to the HTTP client
In the code I put some comments to explain some delicate sections that deserve attention. Furthermore, I simplified the codebase by not handling any error that might happen.
Hope to help in solving your issue, let me know!