Last active
November 4, 2024 10:20
-
-
Save wuriyanto48/1781e941fed381a3add7f08f4dcf590c to your computer and use it in GitHub Desktop.
image compression Golang
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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