imageAve/api/img2color.go
2024-01-08 11:23:08 +08:00

250 lines
5.7 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package handler
import (
"crypto/md5"
"encoding/base64"
"encoding/json"
"fmt"
"image"
"log"
"net/http"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"github.com/go-redis/redis/v8"
"github.com/joho/godotenv"
"github.com/lucasb-eyer/go-colorful"
"github.com/nfnt/resize"
"github.com/disintegration/imaging"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"golang.org/x/net/context"
)
var redisClient *redis.Client
var mongoClient *mongo.Client
var cacheEnabled bool
var useMongoDB bool
var redisDB int
var mongoDB string
var ctx = context.Background()
var colorsCollection *mongo.Collection
var allowedReferers []string
func init() {
currentDir, err := os.Getwd()
if err != nil {
fmt.Printf("获取当前工作目录路径时出错:%v\n", err)
return
}
envFile := filepath.Join(currentDir, ".env")
err = godotenv.Load(envFile)
if err != nil {
fmt.Printf("加载 .env 文件时出错:%v\n", err)
return
}
redisAddr := os.Getenv("REDIS_ADDRESS")
redisPassword := os.Getenv("REDIS_PASSWORD")
cacheEnabledStr := os.Getenv("USE_REDIS_CACHE")
redisDBStr := os.Getenv("REDIS_DB")
mongoDB = os.Getenv("MONGO_DB")
mongoURI := os.Getenv("MONGO_URI")
referers := os.Getenv("ALLOWED_REFERERS")
redisDB, err = strconv.Atoi(redisDBStr)
if err != nil {
redisDB = 0
}
redisClient = redis.NewClient(&redis.Options{
Addr: redisAddr,
Password: redisPassword,
DB: redisDB,
})
cacheEnabled = cacheEnabledStr == "true"
useMongoDBStr := os.Getenv("USE_MONGODB")
useMongoDB = useMongoDBStr == "true"
if useMongoDB {
log.Println("连接到MongoDB...")
clientOptions := options.Client().ApplyURI(mongoURI)
mongoClient, err = mongo.Connect(ctx, clientOptions)
if err != nil {
log.Fatalf("连接到MongoDB时出错%v", err)
}
log.Println("已连接到MongoDB")
colorsCollection = mongoClient.Database(mongoDB).Collection("colors")
}
allowedReferers = parseReferers(referers)
}
func calculateMD5Hash(data []byte) string {
hash := md5.Sum(data)
return base64.StdEncoding.EncodeToString(hash[:])
}
func extractMainColor(imgURL string) (string, error) {
md5Hash := calculateMD5Hash([]byte(imgURL))
if cacheEnabled && redisClient != nil {
cachedColor, err := redisClient.Get(ctx, md5Hash).Result()
if err == nil && cachedColor != "" {
return cachedColor, nil
}
}
req, err := http.NewRequest("GET", imgURL, nil)
if err != nil {
return "", err
}
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.253")
client := http.DefaultClient
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
var img image.Image
img, err = imaging.Decode(resp.Body)
if err != nil {
return "", err
}
img = resize.Resize(50, 0, img, resize.Lanczos3)
bounds := img.Bounds()
var r, g, b uint32
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
for x := bounds.Min.X; x < bounds.Max.X; x++ {
c := img.At(x, y)
r0, g0, b0, _ := c.RGBA()
r += r0
g += g0
b += b0
}
}
totalPixels := uint32(bounds.Dx() * bounds.Dy())
averageR := r / totalPixels
averageG := g / totalPixels
averageB := b / totalPixels
mainColor := colorful.Color{R: float64(averageR) / 0xFFFF, G: float64(averageG) / 0xFFFF, B: float64(averageB) / 0xFFFF}
colorHex := mainColor.Hex()
if cacheEnabled && redisClient != nil {
_, err := redisClient.Set(ctx, md5Hash, colorHex, 0).Result()
if err != nil {
log.Printf("将结果存储在缓存中时出错:%v\n", err)
}
}
if useMongoDB && colorsCollection != nil {
_, err := colorsCollection.InsertOne(ctx, bson.M{
"url": imgURL,
"color": colorHex,
})
if err != nil {
log.Printf("将结果存储在MongoDB中时出错%v\n", err)
}
}
return colorHex, nil
}
func handleImageColor(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Referer")
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusOK)
return
}
referer := r.Header.Get("Referer")
if !isRefererAllowed(referer) {
http.Error(w, "禁止访问", http.StatusForbidden)
return
}
imgURL := r.URL.Query().Get("img")
if imgURL == "" {
http.Error(w, "缺少img参数", http.StatusBadRequest)
return
}
color, err := extractMainColor(imgURL)
if err != nil {
http.Error(w, fmt.Sprintf("提取主色调失败:%v", err), http.StatusInternalServerError)
return
}
data := map[string]string{
"RGB": color,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(data)
}
func Handler(w http.ResponseWriter, r *http.Request) {
handleImageColor(w, r)
}
func parseReferers(referers string) []string {
refererList := strings.Split(referers, ",")
for i, referer := range refererList {
refererList[i] = strings.TrimSpace(referer)
}
return refererList
}
func isRefererAllowed(referer string) bool {
if len(allowedReferers) == 0 {
return true
}
for _, allowedReferer := range allowedReferers {
allowedReferer = strings.ReplaceAll(allowedReferer, ".", "\\.")
allowedReferer = strings.ReplaceAll(allowedReferer, "*", ".*")
match, _ := regexp.MatchString(allowedReferer, referer)
if match {
return true
}
}
return false
}
func main() {
http.HandleFunc("/api", Handler)
port := os.Getenv("PORT")
if port == "" {
port = "3000"
}
log.Printf("服务器监听在:%s...\n", port)
err := http.ListenAndServe(":"+port, nil)
if err != nil {
log.Fatalf("启动服务器时出错:%v\n", err)
}
}