go 48 lines · 9 steps

A signed thumbnail endpoint in Gin

A Gin handler that verifies a signed token, decodes resize params, and streams a freshly generated JPEG thumbnail.

Explained by highlit
1func ServeThumbnail(c *gin.Context) {
2 raw := c.Param("token")
3 sig := c.Query("sig")
4 
5 if !validSignature(raw, sig) {
6 c.AbortWithStatus(http.StatusForbidden)
7 return
8 }
9 
10 decoded, err := base64.RawURLEncoding.DecodeString(raw)
11 if err != nil {
12 c.AbortWithStatus(http.StatusBadRequest)
13 return
14 }
15 
16 var params struct {
17 Key string `json:"k"`
18 Width int `json:"w"`
19 Height int `json:"h"`
20 }
21 if err := json.Unmarshal(decoded, &params); err != nil {
22 c.AbortWithStatus(http.StatusBadRequest)
23 return
24 }
25 
26 src, err := storage.Open(c.Request.Context(), params.Key)
27 if err != nil {
28 c.AbortWithStatus(http.StatusNotFound)
29 return
30 }
31 defer src.Close()
32 
33 img, _, err := image.Decode(src)
34 if err != nil {
35 c.AbortWithStatus(http.StatusUnprocessableEntity)
36 return
37 }
38 
39 thumb := imaging.Fit(img, params.Width, params.Height, imaging.Lanczos)
40 
41 c.Header("Cache-Control", "public, max-age=31536000, immutable")
42 c.Header("Content-Type", "image/jpeg")
43 c.Status(http.StatusOK)
44 
45 if err := jpeg.Encode(c.Writer, thumb, &jpeg.Options{Quality: 82}); err != nil {
46 c.Error(err)
47 }
48}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Verifying a signature before touching untrusted input keeps forged or tampered requests out of your processing pipeline.
  2. 2Encoding resize parameters into a signed, base64 token lets one URL carry structured data safely.
  3. 3Streaming the encoded image directly to the response writer avoids buffering the whole thumbnail in memory.

Related explainers

Share this explainer

Here's the card — post it anywhere.

A signed thumbnail endpoint in Gin — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code