144 lines
3.9 KiB
Go
144 lines
3.9 KiB
Go
|
|
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) {
|
|||
|
|
// 【修正1:Gin 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},
|
|||
|
|
})
|
|||
|
|
}
|