Skip to content

Instantly share code, notes, and snippets.

@wuriyanto48
Last active November 4, 2024 10:20
Show Gist options
  • Save wuriyanto48/1781e941fed381a3add7f08f4dcf590c to your computer and use it in GitHub Desktop.
Save wuriyanto48/1781e941fed381a3add7f08f4dcf590c to your computer and use it in GitHub Desktop.
image compression Golang
package main
import (
"bytes"
"encoding/base64"
"errors"
"fmt"
"image"
"image/jpeg"
_ "image/png"
"io"
"math"
"os"
"golang.org/x/image/draw"
)
func main() {
fileIn, err := os.Open("/home/code/go-compress/Sample-png-image-5mb.png")
if err != nil {
fmt.Printf("error open file | %s\n", err.Error())
os.Exit(1)
}
defer fileIn.Close()
base64ImageStr, err := imageToBase64String(fileIn)
if err != nil {
fmt.Printf("imageToBase64String error | %s\n", err.Error())
os.Exit(1)
}
img, err := imageFromBase64String(base64ImageStr)
if err != nil {
fmt.Printf("imageFromBase64String error | %s\n", err.Error())
os.Exit(1)
}
var buf bytes.Buffer
imageRGBA := resizeImage(img, 500, 0)
err = imageToJpeg(imageRGBA, &buf, 400000)
if err != nil {
fmt.Printf("error in imageToJpeg | %s\n", err.Error())
os.Exit(1)
}
base64ImageStrOut, err := imageToBase64String(&buf)
if err != nil {
fmt.Printf("imageToBase64String error | %s\n", err.Error())
os.Exit(1)
}
fileOut, err := os.Create("out.txt")
if err != nil {
fmt.Printf("error create file | %s\n", err.Error())
os.Exit(1)
}
defer fileOut.Close()
if _, err := fileOut.WriteString(base64ImageStrOut); err != nil {
fmt.Printf("error writing to file | %s\n", err.Error())
os.Exit(1)
}
}
func resizeImage(img image.Image, width, height int) *image.RGBA {
bounds := img.Bounds()
if width == 0 && height == 0 {
width, height = 500, 500
} else if width == 0 {
width = bounds.Dx() * height / bounds.Dy()
} else if height == 0 {
height = bounds.Dy() * width / bounds.Dx()
}
scaleFactor := float64(500) / math.Max(float64(width), float64(height))
width, height = int(float64(width)*scaleFactor), int(float64(height)*scaleFactor)
newImg := image.NewRGBA(image.Rect(0, 0, width, height))
draw.CatmullRom.Scale(newImg, newImg.Bounds(), img, bounds, draw.Over, nil)
return newImg
}
func imageToJpeg(in image.Image, out io.Writer, maxSize int64) error {
quality := 100
var buf bytes.Buffer
for quality > 10 {
buf.Reset()
if err := jpeg.Encode(&buf, in, &jpeg.Options{Quality: quality}); err != nil {
return err
}
if int64(buf.Len()) <= maxSize {
break
}
quality -= 10
}
if quality <= 10 && int64(buf.Len()) > maxSize {
return errors.New("unable to compress image to the specified size")
}
_, err := out.Write(buf.Bytes())
return err
}
func imageToBase64String(in io.Reader) (string, error) {
img, _, err := image.Decode(in)
if err != nil {
return "", fmt.Errorf("failed to decode image: %w", err)
}
var buf bytes.Buffer
if err := jpeg.Encode(&buf, img, &jpeg.Options{Quality: 80}); err != nil {
return "", fmt.Errorf("failed to encode image to JPEG: %w", err)
}
return base64.StdEncoding.EncodeToString(buf.Bytes()), nil
}
func imageFromBase64String(encodedStr string) (image.Image, error) {
imgData, err := base64.StdEncoding.DecodeString(encodedStr)
if err != nil {
return nil, fmt.Errorf("failed to decode base64 string: %w", err)
}
imgReader := bytes.NewReader(imgData)
img, _, err := image.Decode(imgReader)
if err != nil {
return nil, fmt.Errorf("failed to decode image: %w", err)
}
return img, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment