32bf0415/html_components/images.go
1970-01-01 00:00:00 +00:00

132 lines
3.6 KiB
Go

package html_components
import (
"bytes"
"todo/crud"
"fmt"
. "github.com/labstack/echo/v4"
"golang.org/x/image/draw"
"image"
"image/jpeg"
"image/png"
"io"
"math"
"net/http"
"os"
)
const todotransPixel = "\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x21\xF9\x04\x01\x00\x00\x00\x00\x2C\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x44\x01\x00\x3B"
func ReturnEmptyPixel(c Context) error {
return c.Blob(http.StatusOK, "image/gif", []byte(todotransPixel))
}
func ReturnImage(c Context, path string) error {
height := ParseIntWithDefault(c.QueryParam("height"), -1)
width := ParseIntWithDefault(c.QueryParam("width"), -1)
tmpFilePath := getTmpFilePath(path, width, height)
// if tmp file path exists
if _, err := os.Stat(tmpFilePath); err == nil {
// read from the tmp file
return sendImage(c, tmpFilePath)
} else {
filePath := "files/" + path
if width > 0 || height > 0 {
data, err := os.ReadFile(filePath)
mimeType := http.DetectContentType(data)
if err != nil {
return ReturnEmptyPixel(c)
}
return getResized(c, data, mimeType, width, height, path)
}
crud.Debug("returning the full image: ", filePath)
return sendImage(c, filePath)
}
}
func sendImage(c Context, filePath string) error {
data, err := os.ReadFile(filePath)
if err != nil {
return ReturnEmptyPixel(c)
}
mimeType := http.DetectContentType(data)
return c.Blob(http.StatusOK, mimeType, data)
}
func getResized(c Context, data []byte, mimeType string, width int, height int, path string) error {
// create thumbnail and save to tmp
err := os.MkdirAll("tmp", os.ModePerm)
if err != nil {
crud.Error("Failed to create tmp directory: ", err)
return c.Blob(http.StatusOK, mimeType, data)
}
buffer, err := createThumbnailBuffer(c, data, mimeType, width, height)
if err != nil {
crud.Error("Failed to create thumbnail: ", err)
return c.Blob(http.StatusOK, mimeType, data)
}
filePath := getTmpFilePath(path, width, height)
err = CreateFile(filePath, buffer)
if err != nil {
crud.Error("Failed to save thumbnail: ", err)
return sendImage(c, path)
}
return sendImage(c, filePath)
}
func getTmpFilePath(path string, width int, height int) string {
tmpFilePath := fmt.Sprint("tmp/", path, "_", width, "_", height)
return tmpFilePath
}
func createThumbnailBuffer(c Context, data []byte, mimeType string, width int, height int) (*bytes.Buffer, error) {
reader := bytes.NewReader(data)
buffer := new(bytes.Buffer)
err := ThumbnailImage(reader, buffer, mimeType, width, height)
if err != nil {
return nil, err
}
return buffer, nil
}
// ThumbnailImage https://roeber.dev/posts/resize-an-image-in-go/
// creates a resized image from the reader and writes it to
// the writer. The mimetype determines how the image will be decoded
// and must be either "image/jpeg" or "image/png". The desired width
// of the ThumbnailImage is specified in pixels, and the resulting height
// will be calculated to preserve the aspect ratio.
func ThumbnailImage(r io.Reader, w io.Writer, mimetype string, width int, height int) error {
var src image.Image
var err error
switch mimetype {
case "image/jpeg":
src, err = jpeg.Decode(r)
case "image/png":
src, err = png.Decode(r)
}
if err != nil {
return err
}
ratio := (float64)(src.Bounds().Max.Y) / (float64)(src.Bounds().Max.X)
if height == -1 {
height = int(math.Round(float64(width) * ratio))
}
if width == -1 {
width = int(math.Round(float64(height) / ratio))
}
dst := image.NewRGBA(image.Rect(0, 0, width, height))
draw.CatmullRom.Scale(dst, dst.Rect, src, src.Bounds(), draw.Over, nil)
err = jpeg.Encode(w, dst, nil)
if err != nil {
return err
}
return nil
}