update
This commit is contained in:
parent
afa0cbf9c0
commit
bc9ba4855a
@ -28,12 +28,22 @@ func main() {
|
||||
cfg.APIKey,
|
||||
)
|
||||
|
||||
uploadHandler := handler.NewUploadHandler(
|
||||
cfg.R2AccessKey,
|
||||
cfg.R2SecretKey,
|
||||
cfg.R2Bucket,
|
||||
cfg.R2Endpoint,
|
||||
cfg.R2CustomDomain,
|
||||
)
|
||||
|
||||
// Setup Gin router
|
||||
r := gin.Default()
|
||||
|
||||
// Register routes
|
||||
r.POST("/ocr", ocrHandler.HandleOCR)
|
||||
r.POST("/rate", rateHandler.HandleRate)
|
||||
// upload file to server
|
||||
r.POST("/upload", uploadHandler.HandleUpload)
|
||||
|
||||
// Start server
|
||||
if err := r.Run("localhost:8080"); err != nil {
|
||||
|
||||
2
go.mod
2
go.mod
@ -3,6 +3,7 @@ module tencenthw
|
||||
go 1.23.4
|
||||
|
||||
require (
|
||||
github.com/aws/aws-sdk-go v1.55.5
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/google/generative-ai-go v0.19.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
@ -35,6 +36,7 @@ require (
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
|
||||
8
go.sum
8
go.sum
@ -10,6 +10,8 @@ cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4
|
||||
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
||||
cloud.google.com/go/longrunning v0.5.7 h1:WLbHekDbjK1fVFD3ibpFFVoyizlLRl73I7YKuAKilhU=
|
||||
cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng=
|
||||
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
|
||||
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
@ -59,6 +61,10 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gT
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
@ -145,6 +151,8 @@ google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/g
|
||||
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@ -10,6 +10,11 @@ type Config struct {
|
||||
TencentSecretKey string
|
||||
GeminiAPIKey string
|
||||
APIKey string
|
||||
R2AccessKey string
|
||||
R2SecretKey string
|
||||
R2Bucket string
|
||||
R2Endpoint string
|
||||
R2CustomDomain string
|
||||
}
|
||||
|
||||
func LoadConfig() (*Config, error) {
|
||||
@ -22,5 +27,10 @@ func LoadConfig() (*Config, error) {
|
||||
TencentSecretKey: os.Getenv("TENCENT_SECRET_KEY"),
|
||||
GeminiAPIKey: os.Getenv("GEMINI_API_KEY"),
|
||||
APIKey: os.Getenv("API_KEY"),
|
||||
R2AccessKey: os.Getenv("R2_ACCESS_KEY"),
|
||||
R2SecretKey: os.Getenv("R2_SECRET_KEY"),
|
||||
R2Bucket: os.Getenv("R2_BUCKET"),
|
||||
R2Endpoint: os.Getenv("R2_ENDPOINT"),
|
||||
R2CustomDomain: os.Getenv("R2_CUSTOM_DOMAIN"),
|
||||
}, nil
|
||||
}
|
||||
130
pkg/handler/upload.go
Normal file
130
pkg/handler/upload.go
Normal file
@ -0,0 +1,130 @@
|
||||
// 上传文件到cloudflare R2
|
||||
package handler
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
)
|
||||
|
||||
type UploadHandler struct {
|
||||
accessKey string
|
||||
secretKey string
|
||||
bucket string
|
||||
endpoint string
|
||||
customDomain string
|
||||
}
|
||||
|
||||
type UploadRequest struct {
|
||||
File string `json:"file" binding:"required"`
|
||||
APIKey string `json:"apikey" binding:"required"`
|
||||
}
|
||||
|
||||
type UploadResponse struct {
|
||||
ImageURL string `json:"image_url"`
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
func NewUploadHandler(accessKey, secretKey, bucket, endpoint, customDomain string) *UploadHandler {
|
||||
return &UploadHandler{
|
||||
accessKey: accessKey,
|
||||
secretKey: secretKey,
|
||||
bucket: bucket,
|
||||
endpoint: endpoint,
|
||||
customDomain: customDomain,
|
||||
}
|
||||
}
|
||||
// 上传文件到cloudflare R2。判断文件是否是图片,如果是图片,则上传到R2,并返回图片的url,如果不是图片,则返回错误。
|
||||
// 图片大小限制为10M,图片格式为jpg, jpeg, png, gif, bmp, tiff, webp
|
||||
// HandleUpload 上传文件到Cloudflare R2
|
||||
func (h *UploadHandler) HandleUpload(c *gin.Context) {
|
||||
// 解析请求体
|
||||
file, header, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to read file from request"})
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 读取文件内容
|
||||
fileBuffer := make([]byte, header.Size)
|
||||
_, err = file.Read(fileBuffer)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to read file content"})
|
||||
return
|
||||
}
|
||||
|
||||
// 验证文件类型
|
||||
contentType := http.DetectContentType(fileBuffer)
|
||||
if !isImage(contentType) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid file type. Only images are allowed"})
|
||||
return
|
||||
}
|
||||
|
||||
// 验证文件大小
|
||||
if header.Size > 10<<20 { // 10MB
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "File size exceeds the limit of 10MB"})
|
||||
return
|
||||
}
|
||||
|
||||
// 上传文件到R2
|
||||
imageURL, err := h.uploadToR2(fileBuffer, header.Filename, contentType)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to upload file to R2: %v", err)})
|
||||
return
|
||||
}
|
||||
|
||||
// 返回结果
|
||||
response := UploadResponse{
|
||||
ImageURL: imageURL,
|
||||
Success: true,
|
||||
}
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
// uploadToR2 上传文件到Cloudflare R2
|
||||
func (h *UploadHandler) uploadToR2(file []byte, fileName, contentType string) (string, error) {
|
||||
// 创建S3会话
|
||||
sess, err := session.NewSession(&aws.Config{
|
||||
Endpoint: aws.String(h.endpoint),
|
||||
Region: aws.String("auto"),
|
||||
Credentials: credentials.NewStaticCredentials(h.accessKey, h.secretKey, ""),
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create S3 session: %v", err)
|
||||
}
|
||||
|
||||
// 创建S3服务客户端
|
||||
svc := s3.New(sess)
|
||||
|
||||
// 上传文件到R2
|
||||
_, err = svc.PutObject(&s3.PutObjectInput{
|
||||
Bucket: aws.String(h.bucket),
|
||||
Key: aws.String(fileName),
|
||||
Body: bytes.NewReader(file),
|
||||
ContentType: aws.String(contentType),
|
||||
ACL: aws.String("public-read"), // 设置文件为公开可读
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to upload file to R2: %v", err)
|
||||
}
|
||||
|
||||
// 生成文件的URL
|
||||
imageURL := fmt.Sprintf("https://%s/%s", h.customDomain, fileName)
|
||||
return imageURL, nil
|
||||
}
|
||||
|
||||
// isImage 检查文件是否是图片
|
||||
func isImage(contentType string) bool {
|
||||
allowedTypes := []string{"image/jpeg", "image/png", "image/gif", "image/bmp", "image/tiff", "image/webp"}
|
||||
for _, t := range allowedTypes {
|
||||
if contentType == t {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user