From 2afbafa1921853d4dae9c2a4c86d33b2d90a361c Mon Sep 17 00:00:00 2001 From: mayiming <1627832236@qq.com> Date: Sat, 4 Oct 2025 20:48:50 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=90=8E=E7=AB=AF=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 + server/.gitignore | 1 + server/.idea/.gitignore | 8 + server/config/config.go | 61 +++++++ server/config/config.yaml | 22 +++ server/handler/uploadimg/upload.go | 106 ++++++++++++ server/main.go | 163 ++---------------- server/middleware/cors.go | 39 +++++ server/router/router.go | 26 +++ .../cache/download/golang.org/x/mod/@v/list | 1 + .../cache/download/golang.org/x/sync/@v/list | 1 + .../cache/download/golang.org/x/sys/@v/list | 1 + .../cache/download/golang.org/x/tools/@v/list | 1 + 13 files changed, 289 insertions(+), 146 deletions(-) create mode 100644 .gitignore create mode 100644 server/.gitignore create mode 100644 server/.idea/.gitignore create mode 100644 server/config/config.go create mode 100644 server/config/config.yaml create mode 100644 server/handler/uploadimg/upload.go create mode 100644 server/middleware/cors.go create mode 100644 server/router/router.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..4ff5d6bb --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +server/.idea/inspectionProfiles/Project_Default.xml +server/.idea/server.iml +server/.idea/modules.xml +server/.idea/vcs.xml +server/server/pkg/ diff --git a/server/.gitignore b/server/.gitignore new file mode 100644 index 00000000..130c77dd --- /dev/null +++ b/server/.gitignore @@ -0,0 +1 @@ +server/pkg/ \ No newline at end of file diff --git a/server/.idea/.gitignore b/server/.idea/.gitignore new file mode 100644 index 00000000..35410cac --- /dev/null +++ b/server/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/server/config/config.go b/server/config/config.go new file mode 100644 index 00000000..af7f26f6 --- /dev/null +++ b/server/config/config.go @@ -0,0 +1,61 @@ +package config + +import ( + "os" + + "gopkg.in/yaml.v3" +) + +// Config 应用配置结构体 +type Config struct { + Server ServerConfig `yaml:"server"` + OSS OSSConfig `yaml:"oss"` + MySQL MySQLConfig `yaml:"mysql"` + Upload UploadConfig `yaml:"upload"` +} + +// ServerConfig 服务器配置 +type ServerConfig struct { + Port string `yaml:"port"` + AllowedOrigins []string `yaml:"allowed_origins"` +} + +// OSSConfig 阿里云OSS配置 +type OSSConfig struct { + Endpoint string `yaml:"endpoint"` + BucketName string `yaml:"bucket_name"` + AccessKeyID string `yaml:"access_key_id"` + AccessKeySecret string `yaml:"access_key_secret"` + ObjectPrefix string `yaml:"object_prefix"` +} + +// MySQLConfig MySQL数据库配置 +type MySQLConfig struct { + Host string `yaml:"host"` + Port int `yaml:"port"` + Username string `yaml:"username"` + Password string `yaml:"password"` + Database string `yaml:"database"` + Charset string `yaml:"charset"` +} + +// UploadConfig 上传配置 +type UploadConfig struct { + AllowImageTypes string `yaml:"allow_image_types"` + MaxFileSize int64 `yaml:"max_file_size"` +} + +// LoadConfig 从文件加载配置 +func LoadConfig(filename string) (*Config, error) { + data, err := os.ReadFile(filename) + if err != nil { + return nil, err + } + + var config Config + if err := yaml.Unmarshal(data, &config); err != nil { + return nil, err + } + + return &config, nil +} diff --git a/server/config/config.yaml b/server/config/config.yaml new file mode 100644 index 00000000..951abdae --- /dev/null +++ b/server/config/config.yaml @@ -0,0 +1,22 @@ +server: + port: 8080 + allowed_origins: ["http://localhost:5173"] + +oss: + endpoint: "https://oss-cn-shenzhen.aliyuncs.com" + bucket_name: "hldr-data" + access_key_id: "LTAI5tMTSfbaaAtYBS2gFkf9" + access_key_secret: "dHfxH4XziDO0C8ppN94kbLUREAw1Dc" + object_prefix: "article/" + +mysql: + host: "localhost" + port: 3306 + username: "root" + password: "your_mysql_password" + database: "article_db" + charset: "utf8mb4" + +upload: + allow_image_types: "image/jpeg,image/png,image/gif,image/webp" + max_file_size: 5242880 # 5MB (5 * 1024 * 1024) diff --git a/server/handler/uploadimg/upload.go b/server/handler/uploadimg/upload.go new file mode 100644 index 00000000..443d2771 --- /dev/null +++ b/server/handler/uploadimg/upload.go @@ -0,0 +1,106 @@ +package handler + +import ( + "fmt" + "net/http" + "strings" + "time" + + "github.com/JACKYMYPERSON/hldrCenter/config" + "github.com/aliyun/aliyun-oss-go-sdk/oss" + "github.com/gin-gonic/gin" +) + +// UploadImageHandler 处理图片上传到OSS +func UploadImageHandler(c *gin.Context, cfg *config.Config) { + // 获取上传的图片文件 + fileHeader, err := c.FormFile("image") + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "code": 400, + "message": "获取图片失败,请重新上传", + "error": err.Error(), + }) + return + } + + // 打开文件 + file, err := fileHeader.Open() + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "code": 500, + "message": "打开图片文件失败", + "error": err.Error(), + }) + return + } + defer file.Close() + + // 检查文件大小 + if fileHeader.Size > cfg.Upload.MaxFileSize { + c.JSON(http.StatusBadRequest, gin.H{ + "code": 400, + "message": fmt.Sprintf("图片大小不能超过 %dMB", cfg.Upload.MaxFileSize>>20), + }) + return + } + + // 检查文件类型 + fileType := fileHeader.Header.Get("Content-Type") + if !strings.Contains(cfg.Upload.AllowImageTypes, fileType) { + c.JSON(http.StatusBadRequest, gin.H{ + "code": 400, + "message": fmt.Sprintf("不支持的图片类型,仅允许:%s", cfg.Upload.AllowImageTypes), + }) + return + } + + // 初始化OSS客户端 + client, err := oss.New(cfg.OSS.Endpoint, cfg.OSS.AccessKeyID, cfg.OSS.AccessKeySecret) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "code": 500, + "message": "初始化OSS客户端失败", + "error": err.Error(), + }) + return + } + + // 获取Bucket + bucket, err := client.Bucket(cfg.OSS.BucketName) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "code": 500, + "message": "获取Bucket失败", + "error": err.Error(), + }) + return + } + + // 生成唯一文件名 + timestamp := time.Now().Format("20060102150405") + filename := fmt.Sprintf("%s_%s", timestamp, fileHeader.Filename) + objectKey := cfg.OSS.ObjectPrefix + filename // OSS中的完整对象键 + + // 上传文件到OSS + err = bucket.PutObject(objectKey, file) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "code": 500, + "message": "上传图片到OSS失败", + "error": err.Error(), + }) + return + } + + // 生成图片访问URL + host := strings.TrimPrefix(cfg.OSS.Endpoint, "https://") + imageURL := fmt.Sprintf("https://%s.%s/%s", cfg.OSS.BucketName, host, objectKey) + + // 返回成功响应 + c.JSON(http.StatusOK, gin.H{ + "code": 200, + "message": "图片上传成功", + "data": gin.H{"url": imageURL}, + }) +} diff --git a/server/main.go b/server/main.go index 017327b5..640a666f 100644 --- a/server/main.go +++ b/server/main.go @@ -2,159 +2,30 @@ package main import ( "fmt" - "net/http" - "strings" - "time" - "github.com/aliyun/aliyun-oss-go-sdk/oss" - "github.com/gin-gonic/gin" -) - -// 配置常量 -const ( - allowImageTypes = "image/jpeg,image/png,image/gif,image/webp" // 允许的图片类型 - maxFileSize = 5 << 20 // 最大上传文件大小(5MB) -) - -// 阿里云 OSS 配置(请替换为你自己的信息) -const ( - OSS_ENDPOINT = "https://oss-cn-shenzhen.aliyuncs.com" // 你的OSS地域节点 - OSS_BUCKET_NAME = "hldr-data" // 你的Bucket名称 - OSS_ACCESS_KEY_ID = "LTAI5tMTSfbaaAtYBS2gFkf9" // 你的AccessKeyId - OSS_ACCESS_KEY_SECRET = "dHfxH4XziDO0C8ppN94kbLUREAw1Dc" // 你的AccessKeySecret - OSS_OBJECT_PREFIX = "article/" // OSS中的文件前缀(类似文件夹) + "github.com/JACKYMYPERSON/hldrCenter/config" // 替换为你的项目模块名 + "github.com/JACKYMYPERSON/hldrCenter/middleware" // 替换为你的项目模块名 + "github.com/JACKYMYPERSON/hldrCenter/router" // 替换为你的项目模块名 ) func main() { - // 1. 初始化 Gin 引擎 - r := gin.Default() + // 加载配置文件 + cfg, err := config.LoadConfig("config/config.yaml") + if err != nil { + fmt.Printf("加载配置失败:%v\n", err) + return + } - // 2. 配置跨域 - r.Use(corsMiddleware()) + // 设置路由 + r := router.SetupRouter(cfg) - // 3. 图片上传接口 - r.POST("/api/upload/image", uploadImageHandler) - r.POST("/api/upload/cover", func(c *gin.Context) { - // 直接复用已有的上传逻辑 - uploadImageHandler(c) - }) + // 应用跨域中间件 + r.Use(middleware.CorsMiddleware(&cfg.Server)) - // 4. 启动服务 - fmt.Println("后端服务启动成功,地址:http://localhost:8080") - if err := r.Run(":8080"); err != nil { + // 启动服务 + addr := fmt.Sprintf(":%s", cfg.Server.Port) + fmt.Printf("后端服务启动成功,地址:http://localhost%s\n", addr) + if err := r.Run(addr); err != nil { fmt.Printf("服务启动失败:%v\n", err) } } - -// corsMiddleware 跨域中间件 -func corsMiddleware() gin.HandlerFunc { - return func(c *gin.Context) { - c.Writer.Header().Set("Access-Control-Allow-Origin", "http://localhost:5173") - c.Writer.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type, Accept") - c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") - c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") - - // 处理 OPTIONS 预检请求 - if c.Request.Method == "OPTIONS" { - c.AbortWithStatus(http.StatusOK) - return - } - - c.Next() - } -} - -// uploadImageHandler 图片上传到阿里云OSS处理函数 -func uploadImageHandler(c *gin.Context) { - // 获取上传的图片文件 - fileHeader, err := c.FormFile("image") - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{ - "code": 400, - "message": "获取图片失败,请重新上传", - "error": err.Error(), - }) - return - } - - // 打开文件 - file, err := fileHeader.Open() - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{ - "code": 500, - "message": "打开图片文件失败", - "error": err.Error(), - }) - return - } - defer file.Close() - - // 检查文件大小 - if fileHeader.Size > maxFileSize { - c.JSON(http.StatusBadRequest, gin.H{ - "code": 400, - "message": fmt.Sprintf("图片大小不能超过 %dMB", maxFileSize>>20), - }) - return - } - - // 检查文件类型 - fileType := fileHeader.Header.Get("Content-Type") - if !strings.Contains(allowImageTypes, fileType) { - c.JSON(http.StatusBadRequest, gin.H{ - "code": 400, - "message": fmt.Sprintf("不支持的图片类型,仅允许:%s", allowImageTypes), - }) - return - } - - // 初始化OSS客户端 - client, err := oss.New(OSS_ENDPOINT, OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{ - "code": 500, - "message": "初始化OSS客户端失败", - "error": err.Error(), - }) - return - } - - // 获取Bucket - bucket, err := client.Bucket(OSS_BUCKET_NAME) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{ - "code": 500, - "message": "获取Bucket失败", - "error": err.Error(), - }) - return - } - - // 生成唯一文件名 - timestamp := time.Now().Format("20060102150405") - filename := fmt.Sprintf("%s_%s", timestamp, fileHeader.Filename) - objectKey := OSS_OBJECT_PREFIX + filename // OSS中的完整对象键 - - // 上传文件到OSS - err = bucket.PutObject(objectKey, file) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{ - "code": 500, - "message": "上传图片到OSS失败", - "error": err.Error(), - }) - return - } - - // 生成图片访问URL - // 注意:需要确保Bucket设置了公共读权限,或者生成带签名的URL - host := strings.TrimPrefix(OSS_ENDPOINT, "https://") - imageURL := fmt.Sprintf("https://%s.%s/%s", OSS_BUCKET_NAME, host, objectKey) - - // 返回成功响应 - c.JSON(http.StatusOK, gin.H{ - "code": 200, - "message": "图片上传成功", - "data": gin.H{"url": imageURL}, - }) -} diff --git a/server/middleware/cors.go b/server/middleware/cors.go new file mode 100644 index 00000000..47f05a30 --- /dev/null +++ b/server/middleware/cors.go @@ -0,0 +1,39 @@ +package middleware + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +func CorsMiddleware(cfg *config.ServerConfig) gin.HandlerFunc { + return func(c *gin.Context) { + // 处理跨域请求头 + origin := c.Request.Header.Get("Origin") + if origin != "" && isAllowedOrigin(origin, cfg.AllowedOrigins) { + c.Writer.Header().Set("Access-Control-Allow-Origin", origin) + } + + c.Writer.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, Authorization") + c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") + c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") + + // 处理预检请求 + if c.Request.Method == "OPTIONS" { + c.AbortWithStatus(http.StatusOK) + return + } + + c.Next() + } +} + +// 检查来源是否在允许的列表中 +func isAllowedOrigin(origin string, allowedOrigins []string) bool { + for _, allowed := range allowedOrigins { + if allowed == "*" || allowed == origin { + return true + } + } + return false +} diff --git a/server/router/router.go b/server/router/router.go new file mode 100644 index 00000000..2e0d65b9 --- /dev/null +++ b/server/router/router.go @@ -0,0 +1,26 @@ +package router + +import ( + "github.com/JACKYMYPERSON/hldrCenter/config" + handler "github.com/JACKYMYPERSON/hldrCenter/handler/uploadimg" + "github.com/gin-gonic/gin" +) + +// SetupRouter 初始化路由 +func SetupRouter(cfg *config.Config) *gin.Engine { + r := gin.Default() + + // API路由组 + api := r.Group("/api") + { + // 图片上传路由 + api.POST("/upload/image", func(c *gin.Context) { + handler.UploadImageHandler(c, cfg) + }) + api.POST("/upload/cover", func(c *gin.Context) { + handler.UploadImageHandler(c, cfg) + }) + } + + return r +} diff --git a/server/server/pkg/mod/cache/download/golang.org/x/mod/@v/list b/server/server/pkg/mod/cache/download/golang.org/x/mod/@v/list index 31950dac..4d84d0ec 100644 --- a/server/server/pkg/mod/cache/download/golang.org/x/mod/@v/list +++ b/server/server/pkg/mod/cache/download/golang.org/x/mod/@v/list @@ -1 +1,2 @@ +v0.25.0 v0.28.0 diff --git a/server/server/pkg/mod/cache/download/golang.org/x/sync/@v/list b/server/server/pkg/mod/cache/download/golang.org/x/sync/@v/list index 6b60281a..b6ff47dc 100644 --- a/server/server/pkg/mod/cache/download/golang.org/x/sync/@v/list +++ b/server/server/pkg/mod/cache/download/golang.org/x/sync/@v/list @@ -1 +1,2 @@ +v0.16.0 v0.17.0 diff --git a/server/server/pkg/mod/cache/download/golang.org/x/sys/@v/list b/server/server/pkg/mod/cache/download/golang.org/x/sys/@v/list index 7be637dc..4e015bfd 100644 --- a/server/server/pkg/mod/cache/download/golang.org/x/sys/@v/list +++ b/server/server/pkg/mod/cache/download/golang.org/x/sys/@v/list @@ -1,2 +1,3 @@ +v0.6.0 v0.35.0 v0.36.0 diff --git a/server/server/pkg/mod/cache/download/golang.org/x/tools/@v/list b/server/server/pkg/mod/cache/download/golang.org/x/tools/@v/list index 945a6daa..272a0132 100644 --- a/server/server/pkg/mod/cache/download/golang.org/x/tools/@v/list +++ b/server/server/pkg/mod/cache/download/golang.org/x/tools/@v/list @@ -1 +1,2 @@ +v0.34.0 v0.37.0