go
46 lines · 8 steps
Handling multi-file uploads in Gin
A Gin handler parses a multipart form, validates each file's size, saves them to disk, and reports what landed.
Explained by
highlit
1func UploadFiles(c *gin.Context) {
2 form, err := c.MultipartForm()
3 if err != nil {
4 c.JSON(http.StatusBadRequest, gin.H{"error": "invalid multipart form"})
5 return
6 }
7
8 files := form.File["documents"]
9 if len(files) == 0 {
10 c.JSON(http.StatusBadRequest, gin.H{"error": "no files provided"})
11 return
12 }
13
14 const maxSize = 10 << 20
15 uploaded := make([]gin.H, 0, len(files))
16
17 for _, file := range files {
18 if file.Size > maxSize {
19 c.JSON(http.StatusRequestEntityTooLarge, gin.H{
20 "error": fmt.Sprintf("%s exceeds the 10MB limit", file.Filename),
21 })
22 return
23 }
24
25 name := fmt.Sprintf("%d-%s", time.Now().UnixNano(), filepath.Base(file.Filename))
26 dst := filepath.Join("uploads", name)
27
28 if err := c.SaveUploadedFile(file, dst); err != nil {
29 c.JSON(http.StatusInternalServerError, gin.H{
30 "error": fmt.Sprintf("failed to save %s", file.Filename),
31 })
32 return
33 }
34
35 uploaded = append(uploaded, gin.H{
36 "original": file.Filename,
37 "stored": name,
38 "size": file.Size,
39 })
40 }
41
42 c.JSON(http.StatusCreated, gin.H{
43 "count": len(uploaded),
44 "files": uploaded,
45 })
46}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Validate the whole request shape before touching any file so bad requests fail fast with clear status codes.
- 2Renaming uploads with a timestamp and filepath.Base prevents collisions and strips client-supplied path components.
- 3Accumulating results in a slice lets you return a single structured summary after all files succeed.
Related explainers
php
<?php namespace App\Http\Requests\DataObjects;
Typed request DTOs in Laravel
data-transfer-object
validation
immutability
Intermediate
6 steps
go
package middleware import ( "net/http"
Role-based access control middleware in Gin
middleware
authorization
closures
Intermediate
7 steps
python
from fastapi import FastAPI, Request, status from fastapi.encoders import jsonable_encoder from fastapi.exceptions import RequestValidationError from fastapi.responses import JSONResponse
Redacting sensitive fields in FastAPI errors
validation
error-handling
security
Intermediate
7 steps
go
func UserDashboardCache(rdb *redis.Client, ttl time.Duration) gin.HandlerFunc { return func(c *gin.Context) { claims, ok := c.Get("claims") if !ok {
Per-user response caching in Gin with Redis
middleware
caching
redis
Advanced
9 steps
java
@Component @Order(Ordered.HIGHEST_PRECEDENCE) public class TenantResolutionFilter extends OncePerRequestFilter {
How a tenant-resolution filter works in Spring
multi-tenancy
servlet-filter
thread-local
Intermediate
8 steps
go
package middleware import ( "compress/gzip"
How gzip HTTP middleware works in Go
middleware
compression
object-pooling
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/handling-multi-file-uploads-in-gin-explained-go-6a95/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.