Files
go-123pan-pic/internal/handler/upload.go
sakuradairong 591f521960
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
fix: resolve build, XSS, upload, and config issues
- 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
2026-05-17 15:11:37 +08:00

94 lines
2.4 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 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,
})
}