✨ add face recognition
This commit is contained in:
5
common/constant/face_type.go
Normal file
5
common/constant/face_type.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package constant
|
||||
|
||||
const (
|
||||
FaceTypeSample = "sample"
|
||||
)
|
@@ -13,3 +13,7 @@ const (
|
||||
const (
|
||||
SystemApiNoncePrefix = "system:nonce:"
|
||||
)
|
||||
|
||||
const (
|
||||
FaceSamplePrefix = "face:sample:"
|
||||
)
|
||||
|
24
common/face_recognizer/face_recognizer.go
Normal file
24
common/face_recognizer/face_recognizer.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package face_recognizer
|
||||
|
||||
import (
|
||||
"github.com/Kagami/go-face"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// NewFaceRecognition creates a new instance of FaceRecognition
|
||||
func NewFaceRecognition() *face.Recognizer {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil
|
||||
}
|
||||
modelDir := filepath.Join(dir, "/resources/models/face")
|
||||
rec, err := face.NewRecognizer(modelDir)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil
|
||||
}
|
||||
//defer rec.Close()
|
||||
return rec
|
||||
}
|
84
common/gao_map/amap.go
Normal file
84
common/gao_map/amap.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package gao_map
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/duke-git/lancet/v2/cryptor"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
"github.com/duke-git/lancet/v2/netutil"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type AmapClient struct {
|
||||
key string
|
||||
secret string
|
||||
Location *Location
|
||||
Direction *Direction
|
||||
Place *Place
|
||||
Weather *Weather
|
||||
}
|
||||
|
||||
func NewAmapClient(key, secret string) *AmapClient {
|
||||
amap := &AmapClient{key: key, secret: secret}
|
||||
amap.Location = &Location{client: amap}
|
||||
amap.Direction = &Direction{client: amap}
|
||||
amap.Place = &Place{client: amap}
|
||||
amap.Weather = &Weather{client: amap}
|
||||
|
||||
return amap
|
||||
}
|
||||
|
||||
func (a *AmapClient) DoRequest(url, method string, params url.Values) (resp *http.Response, err error) {
|
||||
params.Set("key", a.key)
|
||||
if a.secret != "" {
|
||||
params.Set("sig", makeSign(a, ¶ms))
|
||||
}
|
||||
|
||||
switch strings.ToUpper(method) {
|
||||
case "GET":
|
||||
resp, err = netutil.HttpGet(url, map[string]string{}, params)
|
||||
case "POST":
|
||||
resp, err = netutil.HttpPost(url, map[string]string{}, params)
|
||||
default:
|
||||
err = errors.New("unknown request method")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, errors.New("amap request error: " + resp.Status)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Status string `json:"status"`
|
||||
Info string `json:"info"`
|
||||
Infocode string `json:"Infocode"`
|
||||
}
|
||||
|
||||
func makeSign(client *AmapClient, values *url.Values) string {
|
||||
keys := maputil.Keys(*values)
|
||||
slice.SortBy(keys, func(a, b string) bool {
|
||||
return a < b
|
||||
})
|
||||
|
||||
data := slice.Map(keys, func(index int, item string) string {
|
||||
return item + "=" + values.Get(item)
|
||||
})
|
||||
str := slice.Join(data, "&") + client.secret
|
||||
return cryptor.Md5String(str)
|
||||
}
|
||||
|
||||
func checkResponse(response Response) error {
|
||||
if response.Status != "1" || response.Infocode != "10000" {
|
||||
return errors.New("amap response error: " + response.Info)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
105
common/gao_map/beans.go
Normal file
105
common/gao_map/beans.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package gao_map
|
||||
|
||||
type WeatherResponse struct {
|
||||
Response
|
||||
|
||||
Lives []struct {
|
||||
Adcode string `json:"adcode"`
|
||||
City string `json:"city"`
|
||||
Province string `json:"province"`
|
||||
Weather string `json:"weather"`
|
||||
Temperature string `json:"temperature"`
|
||||
WindDirection string `json:"winddirection"`
|
||||
WindPower string `json:"windpower"`
|
||||
Humidity string `json:"humidity"`
|
||||
ReportTime string `json:"reporttime"`
|
||||
TemperatureFloat string `json:"temperature_float"`
|
||||
HumidityFloat string `json:"humidity_float"`
|
||||
} `json:"lives"`
|
||||
Forecast []struct {
|
||||
City string `json:"city"`
|
||||
Adcode string `json:"adcode"`
|
||||
Province string `json:"province"`
|
||||
ReportTime string `json:"reporttime"`
|
||||
Casts []struct {
|
||||
Date string `json:"date"`
|
||||
Week string `json:"week"`
|
||||
DayWeather string `json:"dayweather"`
|
||||
NightWeather string `json:"nightweather"`
|
||||
DayTemp string `json:"daytemp"`
|
||||
NightTemp string `json:"nighttemp"`
|
||||
DayWind string `json:"daywind"`
|
||||
NightWind string `json:"nightwind"`
|
||||
DayPower string `json:"daypower"`
|
||||
NightPower string `json:"nightpower"`
|
||||
} `json:"casts"`
|
||||
} `json:"forecast"`
|
||||
}
|
||||
|
||||
type IpResponse struct {
|
||||
Response
|
||||
Province string `json:"province"`
|
||||
City any `json:"city"`
|
||||
Adcode string `json:"adcode"`
|
||||
Rectangle string `json:"rectangle"`
|
||||
}
|
||||
|
||||
type District struct {
|
||||
Citycode any `json:"citycode"`
|
||||
Adcode string `json:"adcode"`
|
||||
Name string `json:"name"`
|
||||
Center string `json:"center"`
|
||||
Level string `json:"level"`
|
||||
Districts []*District `json:"districts"`
|
||||
}
|
||||
|
||||
type AllDistrictResponse struct {
|
||||
Response
|
||||
|
||||
Count int `json:"count,string"`
|
||||
Districts []*District
|
||||
}
|
||||
|
||||
type Geo struct {
|
||||
FormattedAddress string `json:"formatted_address"`
|
||||
Country string `json:"country"`
|
||||
Province string `json:"province"`
|
||||
Citycode string `json:"citycode"`
|
||||
City any `json:"city"`
|
||||
Adcode string `json:"adcode"`
|
||||
Location string `json:"location"`
|
||||
Level string `json:"level"`
|
||||
}
|
||||
|
||||
type GeoResponse struct {
|
||||
Response
|
||||
|
||||
Count string `json:"count"`
|
||||
GeoCodes []Geo `json:"geocodes"`
|
||||
}
|
||||
|
||||
type ReGeoResponse struct {
|
||||
Response
|
||||
|
||||
ReGeoCode struct {
|
||||
FormattedAddress string `json:"formatted_address"`
|
||||
AddressComponent struct {
|
||||
City any `json:"city"`
|
||||
Country string `json:"country"`
|
||||
Province string `json:"province"`
|
||||
Citycode string `json:"citycode"`
|
||||
District any `json:"district"`
|
||||
Adcode string `json:"adcode"`
|
||||
Township string `json:"township"`
|
||||
Towncode string `json:"towncode"`
|
||||
} `json:"addressComponent"`
|
||||
} `json:"regeocode"`
|
||||
}
|
||||
|
||||
type ReGeoRequest struct {
|
||||
Location string
|
||||
//PoiType string
|
||||
Radius int
|
||||
//Extensions string
|
||||
//RoadLevel int
|
||||
}
|
117
common/gao_map/direction.go
Normal file
117
common/gao_map/direction.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package gao_map
|
||||
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
"github.com/duke-git/lancet/v2/netutil"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const drivingUrl = "https://restapi.amap.com/v5/direction/driving" //驾车
|
||||
|
||||
type Direction struct {
|
||||
client *AmapClient
|
||||
}
|
||||
|
||||
type DrivingRequest struct {
|
||||
Origin string
|
||||
Destination string
|
||||
Strategy int
|
||||
Waypoints []string
|
||||
ShowFields string
|
||||
}
|
||||
|
||||
type BicyclingRequest struct {
|
||||
Origin string
|
||||
Destination string
|
||||
ShowFields string
|
||||
}
|
||||
|
||||
type Route struct {
|
||||
Origin string `json:"origin"`
|
||||
Destination string `json:"destination"`
|
||||
Paths []struct {
|
||||
Distance float64 `json:"distance,string"`
|
||||
|
||||
Steps []struct {
|
||||
Cost struct {
|
||||
Duration float64 `json:"duration,string"`
|
||||
Tolls float64 `json:"tolls,string"`
|
||||
TollsDistance float64 `json:"tolls_distance,string"`
|
||||
} `json:"cost"`
|
||||
Cities []struct {
|
||||
Adcode string `json:"adcode"`
|
||||
Citycode string `json:"citycode"`
|
||||
City string `json:"city"`
|
||||
} `json:"cities"`
|
||||
Polyline string `json:"polyline"`
|
||||
} `json:"steps"`
|
||||
} `json:"paths"`
|
||||
}
|
||||
|
||||
type DrivingResponse struct {
|
||||
Response
|
||||
|
||||
Count int `json:"count,string"`
|
||||
Route Route `json:"route"`
|
||||
}
|
||||
|
||||
type BicyclingResponse struct {
|
||||
Response
|
||||
|
||||
Count int `json:"count,string"`
|
||||
Route Route `json:"route"`
|
||||
}
|
||||
|
||||
func (d *Direction) Bicycling(request *BicyclingRequest) (*BicyclingResponse, error) {
|
||||
val := url.Values{}
|
||||
val.Set("origin", request.Origin)
|
||||
val.Set("destination", request.Destination)
|
||||
if !strutil.IsBlank(request.ShowFields) {
|
||||
val.Set("show_fields", request.ShowFields)
|
||||
}
|
||||
|
||||
resp, err := d.client.DoRequest(drivingUrl, "POST", val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var data BicyclingResponse
|
||||
|
||||
if err = netutil.ParseHttpResponse(resp, &data); err != nil {
|
||||
return nil, err
|
||||
} else if err = checkResponse(data.Response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, err
|
||||
}
|
||||
|
||||
func (d *Direction) Driving(request *DrivingRequest) (*DrivingResponse, error) {
|
||||
val := url.Values{}
|
||||
val.Set("origin", request.Origin)
|
||||
val.Set("destination", request.Destination)
|
||||
val.Set("strategy", convertor.ToString(request.Strategy))
|
||||
if len(request.Waypoints) > 0 {
|
||||
val.Set("waypoints", strings.Join(request.Waypoints, ";"))
|
||||
}
|
||||
if !strutil.IsBlank(request.ShowFields) {
|
||||
val.Set("show_fields", request.ShowFields)
|
||||
}
|
||||
|
||||
resp, err := d.client.DoRequest(drivingUrl, "POST", val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var data DrivingResponse
|
||||
|
||||
if err = netutil.ParseHttpResponse(resp, &data); err != nil {
|
||||
return nil, err
|
||||
} else if err = checkResponse(data.Response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, err
|
||||
}
|
109
common/gao_map/location.go
Normal file
109
common/gao_map/location.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package gao_map
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/duke-git/lancet/v2/condition"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
"github.com/duke-git/lancet/v2/netutil"
|
||||
"github.com/duke-git/lancet/v2/validator"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
const (
|
||||
ipUrl = "https://restapi.amap.com/v3/ip"
|
||||
geoUrl = "https://restapi.amap.com/v3/geocode/geo"
|
||||
regeoUrl = "https://restapi.amap.com/v3/geocode/regeo"
|
||||
zoneUrl = "https://restapi.amap.com/v3/config/district"
|
||||
)
|
||||
|
||||
type Location struct {
|
||||
client *AmapClient
|
||||
}
|
||||
|
||||
func (l *Location) ChinaDistricts() ([]*District, error) {
|
||||
val := url.Values{}
|
||||
val.Set("keywords", "中国")
|
||||
val.Set("subdistrict", "3")
|
||||
|
||||
resp, err := l.client.DoRequest(zoneUrl, "GET", val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var data AllDistrictResponse
|
||||
|
||||
if err = netutil.ParseHttpResponse(resp, &data); err != nil {
|
||||
return nil, err
|
||||
} else if err = checkResponse(data.Response); err != nil {
|
||||
return nil, err
|
||||
} else if data.Count <= 0 {
|
||||
return nil, errors.New("no record")
|
||||
}
|
||||
|
||||
return data.Districts[0].Districts, err
|
||||
}
|
||||
|
||||
func (l *Location) IpLocation(ip string) (*IpResponse, error) {
|
||||
val := url.Values{}
|
||||
val.Set("ip", ip)
|
||||
|
||||
resp, err := l.client.DoRequest(ipUrl, "GET", val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var data IpResponse
|
||||
|
||||
if err = netutil.ParseHttpResponse(resp, &data); err != nil {
|
||||
return nil, err
|
||||
} else if err = checkResponse(data.Response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
func (l *Location) ReGeo(request *ReGeoRequest) (*ReGeoResponse, error) {
|
||||
if validator.IsEmptyString(request.Location) {
|
||||
return nil, errors.New("location can't empty")
|
||||
}
|
||||
|
||||
val := url.Values{}
|
||||
val.Set("location", request.Location)
|
||||
//if request.PoiType != "" {
|
||||
// val.Set("poitype", request.PoiType)
|
||||
//}
|
||||
//val.Set("extensions", condition.Ternary(request.Extensions == "", "base", request.Extensions))
|
||||
val.Set("radius", convertor.ToString(condition.Ternary(request.Radius == 0, 1000, request.Radius)))
|
||||
//val.Set("roadlevel", convertor.ToString(request.RoadLevel))
|
||||
|
||||
resp, err := l.client.DoRequest(regeoUrl, "GET", val)
|
||||
var data ReGeoResponse
|
||||
|
||||
if err = netutil.ParseHttpResponse(resp, &data); err != nil {
|
||||
return nil, err
|
||||
} else if err = checkResponse(data.Response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
func (l *Location) Geo(address, city string) (*GeoResponse, error) {
|
||||
val := url.Values{}
|
||||
val.Set("address", address)
|
||||
if city != "" {
|
||||
val.Set("city", city)
|
||||
}
|
||||
|
||||
resp, err := l.client.DoRequest(geoUrl, "GET", val)
|
||||
var data GeoResponse
|
||||
|
||||
if err = netutil.ParseHttpResponse(resp, &data); err != nil {
|
||||
return nil, err
|
||||
} else if err = checkResponse(data.Response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
50
common/gao_map/location_test.go
Normal file
50
common/gao_map/location_test.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package gao_map
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const key = ""
|
||||
const secret = ""
|
||||
|
||||
func TestDistricts(t *testing.T) {
|
||||
client := NewAmapClient(key, secret)
|
||||
resp, err := client.Location.ChinaDistricts()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println(resp)
|
||||
}
|
||||
|
||||
func TestIp(t *testing.T) {
|
||||
client := NewAmapClient(key, secret)
|
||||
resp, err := client.Location.IpLocation("121.224.33.99")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(*resp)
|
||||
}
|
||||
|
||||
func TestGeo(t *testing.T) {
|
||||
client := NewAmapClient(key, secret)
|
||||
resp, err := client.Location.Geo("乐东公园一品墅", "")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(*resp)
|
||||
}
|
||||
|
||||
func TestReGeo(t *testing.T) {
|
||||
client := NewAmapClient(key, secret)
|
||||
request := ReGeoRequest{Location: "118.567824,29.306175"}
|
||||
resp, err := client.Location.ReGeo(&request)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(*resp)
|
||||
}
|
82
common/gao_map/place.go
Normal file
82
common/gao_map/place.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package gao_map
|
||||
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/condition"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
"github.com/duke-git/lancet/v2/netutil"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
const placeUrl = "https://restapi.amap.com/v5/place/text" //驾车
|
||||
|
||||
type Place struct {
|
||||
client *AmapClient
|
||||
}
|
||||
|
||||
type PlaceRequest struct {
|
||||
Keywords string
|
||||
Types string
|
||||
Region string
|
||||
CityLimit bool
|
||||
PageSize int
|
||||
PageNum int
|
||||
ShowFields string
|
||||
}
|
||||
|
||||
type PlaceResponse struct {
|
||||
Response
|
||||
|
||||
Count int `json:"count,string"`
|
||||
Pois []struct {
|
||||
Address string `json:"address"`
|
||||
Adcode string `json:"adcode"`
|
||||
Type string `json:"type"`
|
||||
Typecode string `json:"typecode"`
|
||||
CityCode string `json:"citycode"`
|
||||
Name string `json:"name"`
|
||||
Location string `json:"location"`
|
||||
Id string `json:"id"`
|
||||
Business struct {
|
||||
Tel string `json:"tel"`
|
||||
Cost string `json:"cost"`
|
||||
Tag string `json:"tag"`
|
||||
OpentimeToday string `json:"opentime_today"`
|
||||
OpentimeWeek string `json:"opentime_week"`
|
||||
} `json:"business"`
|
||||
Photos []struct {
|
||||
Title string `json:"title"`
|
||||
Url string `json:"url"`
|
||||
} `json:"photos"`
|
||||
} `json:"pois"`
|
||||
}
|
||||
|
||||
func (d *Place) Search(request *PlaceRequest) (*PlaceResponse, error) {
|
||||
val := url.Values{}
|
||||
val.Set("keywords", request.Keywords)
|
||||
val.Set("types", request.Types)
|
||||
if !strutil.IsBlank(request.Region) {
|
||||
val.Set("region", request.Region)
|
||||
}
|
||||
val.Set("city_limit", condition.TernaryOperator(request.CityLimit, "1", "0"))
|
||||
val.Set("page_size", convertor.ToString(condition.TernaryOperator(request.PageSize > 0, request.PageSize, 25)))
|
||||
val.Set("page_num", convertor.ToString(condition.TernaryOperator(request.PageNum > 0, request.PageNum, 1)))
|
||||
if !strutil.IsBlank(request.ShowFields) {
|
||||
val.Set("show_fields", request.ShowFields)
|
||||
}
|
||||
|
||||
resp, err := d.client.DoRequest(placeUrl, "GET", val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var data PlaceResponse
|
||||
|
||||
if err = netutil.ParseHttpResponse(resp, &data); err != nil {
|
||||
return nil, err
|
||||
} else if err = checkResponse(data.Response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, err
|
||||
}
|
32
common/gao_map/weather.go
Normal file
32
common/gao_map/weather.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package gao_map
|
||||
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/netutil"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
const weatherInfoUrl = "https://restapi.amap.com/v3/weather/weatherInfo"
|
||||
|
||||
type Weather struct {
|
||||
client *AmapClient
|
||||
}
|
||||
|
||||
func (w *Weather) Info(adcode, extensions string) (*WeatherResponse, error) {
|
||||
val := url.Values{}
|
||||
val.Set("city", adcode)
|
||||
|
||||
resp, err := w.client.DoRequest(weatherInfoUrl, "GET", val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var data WeatherResponse
|
||||
|
||||
if err = netutil.ParseHttpResponse(resp, &data); err != nil {
|
||||
return nil, err
|
||||
} else if err = checkResponse(data.Response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
@@ -1,7 +1,6 @@
|
||||
package ip2region
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -13,7 +12,6 @@ func NewIP2Region() *xdb.Searcher {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(cwd)
|
||||
dbPath := filepath.Join(cwd, "resources/ip2region", "ip2region.xdb")
|
||||
cBuff, err := xdb.LoadContentFromFile(dbPath)
|
||||
if err != nil {
|
||||
|
@@ -1,65 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// SupportedHashFuncs 定义支持的哈希函数类型
|
||||
var SupportedHashFuncs = map[string]func() hash.Hash{
|
||||
"md5": crypto.MD5.New,
|
||||
"sha1": crypto.SHA1.New,
|
||||
"sha256": crypto.SHA256.New,
|
||||
"sha512": crypto.SHA512.New,
|
||||
}
|
||||
|
||||
// CalculateFileHash 根据指定的哈希算法计算文件的哈希值
|
||||
func CalculateFileHash(filePath string, algorithm string) (string, error) {
|
||||
// 获取对应的哈希函数
|
||||
hashFunc, exists := SupportedHashFuncs[algorithm]
|
||||
if !exists {
|
||||
return "", fmt.Errorf("unsupported hash algorithm: %s", algorithm)
|
||||
}
|
||||
|
||||
// 打开文件
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to open file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 创建哈希对象
|
||||
h := hashFunc()
|
||||
|
||||
// 计算哈希值
|
||||
if _, err := io.Copy(h, file); err != nil {
|
||||
return "", fmt.Errorf("failed to calculate hash: %w", err)
|
||||
}
|
||||
|
||||
// 返回哈希值的十六进制字符串
|
||||
return hex.EncodeToString(h.Sum(nil)), nil
|
||||
}
|
||||
|
||||
// CalculateStreamHash 计算输入流的哈希值
|
||||
func CalculateStreamHash(reader io.Reader, algorithm string) (string, error) {
|
||||
// 获取对应的哈希函数
|
||||
hashFunc, exists := SupportedHashFuncs[algorithm]
|
||||
if !exists {
|
||||
return "", fmt.Errorf("unsupported hash algorithm: %s", algorithm)
|
||||
}
|
||||
|
||||
// 创建哈希对象
|
||||
h := hashFunc()
|
||||
|
||||
// 从输入流计算哈希值
|
||||
if _, err := io.Copy(h, reader); err != nil {
|
||||
return "", fmt.Errorf("failed to calculate hash: %w", err)
|
||||
}
|
||||
|
||||
// 返回哈希值的十六进制字符串
|
||||
return hex.EncodeToString(h.Sum(nil)), nil
|
||||
}
|
24
common/utils/generate_image_hash.go
Normal file
24
common/utils/generate_image_hash.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/corona10/goimagehash"
|
||||
"image"
|
||||
)
|
||||
|
||||
// CalculatePerceptualHash 计算感知哈希
|
||||
func CalculatePerceptualHash(img image.Image) (string, error) {
|
||||
hash, err := goimagehash.PerceptionHash(img)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hash.ToString(), nil
|
||||
}
|
||||
|
||||
// CalculateHash 计算平均哈希
|
||||
func CalculateHash(img image.Image) (uint64, error) {
|
||||
hash, err := goimagehash.AverageHash(img)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return hash.GetHash(), nil
|
||||
}
|
@@ -1,8 +1,13 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
_ "image/jpeg" // 引入 jpeg 解码器
|
||||
_ "image/png" // 引入 png 解码器
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -88,3 +93,25 @@ func Base64ToBytes(base64Str string) ([]byte, error) {
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Base64ToImage 将 Base64 字符串转换为 image.Image 格式
|
||||
func Base64ToImage(base64Str string) (image.Image, error) {
|
||||
// 使用正则表达式去除前缀
|
||||
re := regexp.MustCompile(`^data:image/([a-zA-Z]*);base64,`)
|
||||
// 去除前缀部分
|
||||
base64Str = re.ReplaceAllString(base64Str, "")
|
||||
|
||||
// 解码 Base64 字符串
|
||||
data, err := base64.StdEncoding.DecodeString(base64Str)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode base64 string: %v", err)
|
||||
}
|
||||
|
||||
// 使用 image.Decode 解码字节数据
|
||||
img, _, err := image.Decode(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode image: %v", err)
|
||||
}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user