132 lines
3.6 KiB
Go
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
|
|
}
|