go 56 lines · 9 steps

Handling multipart uploads in Gin

A Gin handler that accepts a file plus JSON metadata in one multipart request, validating each part before storing.

Explained by highlit
1func UploadDocument(c *gin.Context) {
2 fileHeader, err := c.FormFile("file")
3 if err != nil {
4 c.JSON(http.StatusBadRequest, gin.H{"error": "file is required"})
5 return
6 }
7 
8 var meta struct {
9 Title string `json:"title" binding:"required"`
10 Tags []string `json:"tags"`
11 OwnerID int64 `json:"owner_id" binding:"required"`
12 IsPublic bool `json:"is_public"`
13 }
14 
15 raw := c.PostForm("metadata")
16 if raw == "" {
17 c.JSON(http.StatusBadRequest, gin.H{"error": "metadata field is required"})
18 return
19 }
20 
21 if err := json.Unmarshal([]byte(raw), &meta); err != nil {
22 c.JSON(http.StatusBadRequest, gin.H{"error": "invalid metadata json"})
23 return
24 }
25 
26 if err := binding.Validator.ValidateStruct(&meta); err != nil {
27 c.JSON(http.StatusUnprocessableEntity, gin.H{"error": err.Error()})
28 return
29 }
30 
31 if fileHeader.Size > maxUploadBytes {
32 c.JSON(http.StatusRequestEntityTooLarge, gin.H{"error": "file too large"})
33 return
34 }
35 
36 src, err := fileHeader.Open()
37 if err != nil {
38 c.JSON(http.StatusInternalServerError, gin.H{"error": "could not read upload"})
39 return
40 }
41 defer src.Close()
42 
43 doc, err := documents.Store(c.Request.Context(), src, documents.Options{
44 Filename: fileHeader.Filename,
45 Title: meta.Title,
46 Tags: meta.Tags,
47 OwnerID: meta.OwnerID,
48 IsPublic: meta.IsPublic,
49 })
50 if err != nil {
51 c.JSON(http.StatusBadGateway, gin.H{"error": "storage failed"})
52 return
53 }
54 
55 c.JSON(http.StatusCreated, doc)
56}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Multipart requests can carry both binary files and structured JSON, so parse and validate each part separately.
  2. 2Validate cheaply first and stream the file only once every check has passed to avoid wasted work.
  3. 3Mapping each failure class to a distinct HTTP status makes the API's errors self-explanatory to clients.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Handling multipart uploads in Gin — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code