summaryrefslogtreecommitdiff
path: root/srv/src/api/assets.go
blob: e94d324b39fdc2f4daf9d558f65b413819b2bf2d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package api

import (
	"bytes"
	"errors"
	"fmt"
	"image"
	"image/jpeg"
	"image/png"
	"io"
	"net/http"
	"path/filepath"
	"strings"

	"github.com/mediocregopher/blog.mediocregopher.com/srv/api/apiutil"
	"github.com/mediocregopher/blog.mediocregopher.com/srv/post"
	"golang.org/x/image/draw"
)

func resizeImage(out io.Writer, in io.Reader, maxWidth float64) error {

	img, format, err := image.Decode(in)
	if err != nil {
		return fmt.Errorf("decoding image: %w", err)
	}

	imgRect := img.Bounds()
	imgW, imgH := float64(imgRect.Dx()), float64(imgRect.Dy())

	if imgW > maxWidth {

		newH := imgH * maxWidth / imgW
		newImg := image.NewRGBA(image.Rect(0, 0, int(maxWidth), int(newH)))

		// Resize
		draw.BiLinear.Scale(
			newImg, newImg.Bounds(), img, img.Bounds(), draw.Over, nil,
		)

		img = newImg
	}

	switch format {
	case "jpeg":
		return jpeg.Encode(out, img, nil)
	case "png":
		return png.Encode(out, img)
	default:
		return fmt.Errorf("unknown image format %q", format)
	}
}

func (a *api) servePostAssetHandler() http.Handler {

	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {

		id := filepath.Base(r.URL.Path)

		maxWidth, err := apiutil.StrToInt(r.FormValue("w"), 0)
		if err != nil {
			apiutil.BadRequest(rw, r, fmt.Errorf("invalid w parameter: %w", err))
			return
		}

		buf := new(bytes.Buffer)

		err = a.params.PostAssetStore.Get(id, buf)

		if errors.Is(err, post.ErrAssetNotFound) {
			http.Error(rw, "Asset not found", 404)
			return
		} else if err != nil {
			apiutil.InternalServerError(
				rw, r, fmt.Errorf("fetching asset with id %q: %w", id, err),
			)
			return
		}

		if maxWidth == 0 {

			if _, err := io.Copy(rw, buf); err != nil {
				apiutil.InternalServerError(
					rw, r,
					fmt.Errorf(
						"copying asset with id %q to response writer: %w",
						id, err,
					),
				)
			}

			return
		}

		switch ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(id), ".")); ext {
		case "jpg", "jpeg", "png":

			if err := resizeImage(rw, buf, float64(maxWidth)); err != nil {
				apiutil.InternalServerError(
					rw, r,
					fmt.Errorf(
						"resizing image with id %q to size %d: %w",
						id, maxWidth, err,
					),
				)
			}

		default:
			apiutil.BadRequest(rw, r, fmt.Errorf("cannot resize file with extension %q", ext))
			return
		}

	})
}