更新缩略图
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"image/jpeg"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/image/draw"
|
||||
|
||||
_ "image/gif"
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
|
||||
_ "golang.org/x/image/webp"
|
||||
)
|
||||
|
||||
// thumbSuffix is appended to an image's object key to form its thumbnail key
|
||||
// ("u/x.png" → "u/x.png.thumb.jpg"). List views load the thumbnail; preview and
|
||||
// download always use the original.
|
||||
const thumbSuffix = ".thumb.jpg"
|
||||
|
||||
// thumbMaxDim bounds the thumbnail's longest side. 512px is crisp for grid
|
||||
// cards / table rows while staying ~20-50 KB as JPEG.
|
||||
const thumbMaxDim = 512
|
||||
|
||||
// ThumbKey returns the thumbnail object key for an image key.
|
||||
func ThumbKey(rel string) string { return rel + thumbSuffix }
|
||||
|
||||
// IsThumbKey reports whether name refers to a thumbnail object, and OrigKey
|
||||
// maps a thumbnail key back to its original image key.
|
||||
func IsThumbKey(name string) bool { return strings.HasSuffix(name, thumbSuffix) }
|
||||
func OrigKey(name string) string { return strings.TrimSuffix(name, thumbSuffix) }
|
||||
|
||||
// makeThumbnail downscales an image to thumbMaxDim (longest side) and encodes
|
||||
// it as JPEG. Images already small enough are re-encoded as-is (so the thumb
|
||||
// object always exists once generated). Returns an error for undecodable input
|
||||
// (e.g. video bytes) — callers treat thumbnailing as best-effort.
|
||||
func makeThumbnail(b []byte) ([]byte, error) {
|
||||
src, _, err := image.Decode(bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bounds := src.Bounds()
|
||||
w, h := bounds.Dx(), bounds.Dy()
|
||||
tw, th := w, h
|
||||
if w > thumbMaxDim || h > thumbMaxDim {
|
||||
if w >= h {
|
||||
tw = thumbMaxDim
|
||||
th = h * thumbMaxDim / w
|
||||
} else {
|
||||
th = thumbMaxDim
|
||||
tw = w * thumbMaxDim / h
|
||||
}
|
||||
if tw < 1 {
|
||||
tw = 1
|
||||
}
|
||||
if th < 1 {
|
||||
th = 1
|
||||
}
|
||||
}
|
||||
// JPEG has no alpha — composite onto white so transparent PNGs don't go black.
|
||||
dst := image.NewRGBA(image.Rect(0, 0, tw, th))
|
||||
draw.Draw(dst, dst.Bounds(), image.White, image.Point{}, draw.Src)
|
||||
draw.CatmullRom.Scale(dst, dst.Bounds(), src, bounds, draw.Over, nil)
|
||||
var out bytes.Buffer
|
||||
if err := jpeg.Encode(&out, dst, &jpeg.Options{Quality: 78}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
Reference in New Issue
Block a user