修改后端初始化
This commit is contained in:
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -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/
|
||||
1
server/.gitignore
vendored
Normal file
1
server/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
server/pkg/
|
||||
8
server/.idea/.gitignore
generated
vendored
Normal file
8
server/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
61
server/config/config.go
Normal file
61
server/config/config.go
Normal file
@@ -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
|
||||
}
|
||||
22
server/config/config.yaml
Normal file
22
server/config/config.yaml
Normal file
@@ -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)
|
||||
106
server/handler/uploadimg/upload.go
Normal file
106
server/handler/uploadimg/upload.go
Normal file
@@ -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},
|
||||
})
|
||||
}
|
||||
163
server/main.go
163
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},
|
||||
})
|
||||
}
|
||||
|
||||
39
server/middleware/cors.go
Normal file
39
server/middleware/cors.go
Normal file
@@ -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
|
||||
}
|
||||
26
server/router/router.go
Normal file
26
server/router/router.go
Normal file
@@ -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
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
v0.25.0
|
||||
v0.28.0
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
v0.16.0
|
||||
v0.17.0
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
v0.6.0
|
||||
v0.35.0
|
||||
v0.36.0
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
v0.34.0
|
||||
v0.37.0
|
||||
|
||||
Reference in New Issue
Block a user