package html_components import ( "bytes" "test/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 }