Some checks failed
Go Build & Release / build (amd64, imagehost-linux-amd64, linux) (push) Has been cancelled
Go Build & Release / build (amd64, imagehost-macos-amd64, darwin) (push) Has been cancelled
Go Build & Release / build (amd64, imagehost-windows-amd64.exe, windows) (push) Has been cancelled
Go Build & Release / build (arm64, imagehost-linux-arm64, linux) (push) Has been cancelled
Go Build & Release / build (arm64, imagehost-macos-arm64, darwin) (push) Has been cancelled
Go Build & Release / docker (push) Has been cancelled
- go.mod: fix version 1.25.0 -> 1.19, use real dependency versions - go.sum: regenerate from resolved real dependencies - static/js/main.js: eliminate innerHTML XSS in renderGallery/uploadFile; add error toast for non-403 responses; created_at fallback - internal/handler/upload.go: add MIME magic validation and 50MB file limit - internal/pan123/model.go: unify FileListReq JSON tags (parentFileID/lastFileID) - internal/service/image_service.go: TrimRight -> TrimSuffix - internal/service/upload_service.go: TrimRight -> TrimSuffix; implement multi-slice upload using io.SectionReader
94 lines
2.4 KiB
Go
94 lines
2.4 KiB
Go
package handler
|
||
|
||
import (
|
||
"log"
|
||
"net/http"
|
||
"path/filepath"
|
||
"strings"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
"imagehost/internal/service"
|
||
)
|
||
|
||
type UploadHandler struct {
|
||
uploadSvc *service.UploadService
|
||
}
|
||
|
||
func NewUploadHandler(svc *service.UploadService) *UploadHandler {
|
||
return &UploadHandler{uploadSvc: svc}
|
||
}
|
||
|
||
func (h *UploadHandler) HandleUpload(c *gin.Context) {
|
||
fileHeader, err := c.FormFile("file")
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "无法收到图片文件: " + err.Error()})
|
||
return
|
||
}
|
||
|
||
const maxFileSize int64 = 50 << 20 // 50MB
|
||
if fileHeader.Size > maxFileSize {
|
||
c.JSON(http.StatusRequestEntityTooLarge, gin.H{
|
||
"code": 413,
|
||
"message": "文件过大,最大支持 50MB。",
|
||
})
|
||
return
|
||
}
|
||
|
||
ext := strings.ToLower(filepath.Ext(fileHeader.Filename))
|
||
validExts := map[string]bool{
|
||
".jpg": true, ".jpeg": true, ".png": true,
|
||
".gif": true, ".webp": true, ".svg": true, ".bmp": true,
|
||
}
|
||
if !validExts[ext] {
|
||
c.JSON(http.StatusUnsupportedMediaType, gin.H{
|
||
"code": 415,
|
||
"message": "仅支持上传图片格式(jpg/png/gif/webp/svg/bmp)。",
|
||
})
|
||
return
|
||
}
|
||
|
||
if !strings.HasPrefix(fileHeader.Header.Get("Content-Type"), "image/") {
|
||
c.JSON(http.StatusUnsupportedMediaType, gin.H{"code": 415, "message": "不支持的 Content-Type 类型。"})
|
||
return
|
||
}
|
||
// MIME 魔数校验:读取前 512 字节确认真实文件类型
|
||
fileReader, err := fileHeader.Open()
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "无法读取文件: " + err.Error()})
|
||
return
|
||
}
|
||
|
||
buf := make([]byte, 512)
|
||
_, err = fileReader.Read(buf)
|
||
fileReader.Close()
|
||
if err != nil && err.Error() != "EOF" {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "读取文件头失败: " + err.Error()})
|
||
return
|
||
}
|
||
|
||
mimeType := http.DetectContentType(buf)
|
||
if !strings.HasPrefix(mimeType, "image/") {
|
||
c.JSON(http.StatusUnsupportedMediaType, gin.H{
|
||
"code": 415,
|
||
"message": "文件真实类型不是图片(" + mimeType + ")。",
|
||
})
|
||
return
|
||
}
|
||
|
||
log.Printf("[上传] 接收文件: %s (MIME: %s)", fileHeader.Filename, mimeType)
|
||
|
||
fileInfo, err := h.uploadSvc.UploadFile(fileHeader)
|
||
if err != nil {
|
||
log.Printf("[上传] 失败: %v", err)
|
||
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": err.Error()})
|
||
return
|
||
}
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"code": 0,
|
||
"message": "success",
|
||
"data": fileInfo,
|
||
"url": fileInfo.UserSelfURL,
|
||
})
|
||
}
|