Files
hldrCenter/server/main.go

144 lines
3.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
"fmt"
"net/http"
"os"
"path"
"strings"
"time"
"github.com/gin-gonic/gin"
)
// 配置常量
const (
uploadDir = "./uploads" // 图片保存目录
allowImageTypes = "image/jpeg,image/png,image/gif,image/webp" // 允许的图片类型
maxFileSize = 5 << 20 // 最大上传文件大小5MB
)
func main() {
// 1. 初始化 Gin 引擎
r := gin.Default()
// 2. 配置跨域
r.Use(corsMiddleware())
// 3. 初始化上传目录
if err := initUploadDir(); err != nil {
fmt.Printf("初始化上传目录失败:%v\n", err)
return
}
// 4. 静态文件路由(访问上传的图片)
r.Static("/uploads", uploadDir)
// 5. 图片上传接口(核心修正在这里)
r.POST("/api/upload/image", uploadImageHandler)
// 6. 启动服务
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) {
// 关键修正:替换 * 为前端实际域名(本地开发是 http://localhost:5173
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")
// 允许携带 Cookie与前端 credentials: 'include' 对应)
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
// 处理 OPTIONS 预检请求
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(http.StatusOK)
return
}
c.Next()
}
}
// initUploadDir 初始化上传目录
func initUploadDir() error {
_, err := os.Stat(uploadDir)
if os.IsNotExist(err) {
return os.MkdirAll(uploadDir, 0755)
}
return err
}
// uploadImageHandler 图片上传处理函数(关键修正部分)
func uploadImageHandler(c *gin.Context) {
// 【修正1Gin v1.9+ 中 FormFile 只返回 2 个值fileHeader 和 err】
fileHeader, err := c.FormFile("image")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"message": "获取图片失败,请重新上传",
"error": err.Error(),
})
return
}
// 【修正2通过 fileHeader.Open() 获取 file 对象(原 3 返回值中的 file
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
}
// 生成唯一文件名
timestamp := time.Now().Format("20060102150405")
filename := fmt.Sprintf("%s_%s", timestamp, fileHeader.Filename)
savePath := path.Join(uploadDir, filename)
// 【修正3保存文件时使用 fileHeader原代码用 file 也可,但 Gin 推荐用 fileHeader
if err := c.SaveUploadedFile(fileHeader, savePath); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"message": "图片保存失败,请重试",
"error": err.Error(),
})
return
}
// 生成图片访问 URL
imageURL := fmt.Sprintf("http://%s/uploads/%s", c.Request.Host, filename)
// 返回成功响应
c.JSON(http.StatusOK, gin.H{
"code": 200,
"message": "图片上传成功",
"data": gin.H{"url": imageURL},
})
}