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中的文件前缀(类似文件夹) ) func main() { // 1. 初始化 Gin 引擎 r := gin.Default() // 2. 配置跨域 r.Use(corsMiddleware()) // 3. 图片上传接口 r.POST("/api/upload/image", uploadImageHandler) r.POST("/api/upload/cover", func(c *gin.Context) { // 直接复用已有的上传逻辑 uploadImageHandler(c) }) // 4. 启动服务 fmt.Println("后端服务启动成功,地址:http://localhost:8080") if err := r.Run(":8080"); 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}, }) }