package photosapi import ( "crypto/sha256" "encoding/hex" "errors" "fmt" "io" "net/http" "os" "github.com/gin-gonic/gin" "github.com/google/uuid" ) var ( ErrUploadNotExists = errors.New("upload id doesn't exists") ) func (s *Service) UploadCreate(c *gin.Context) { sha, err := uuid.NewRandom() if err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } if err := s.Storage.Create(StorageTmp, sha.String()); err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } c.JSON(http.StatusCreated, gin.H{ "status": "success", "upload_id": sha.String(), }) } type UploadUri struct { Id string `uri:"upload_id" binding:"required,uuid"` } type UploadPartQuery struct { Part uint `form:"part" binding:"required"` PartSha256 string `form:"sha256" binding:"required,sha256"` } func (s *Service) UploadPart(c *gin.Context) { var ( upload UploadUri uploadPart UploadPartQuery ) if c.BindUri(&upload) != nil || c.BindQuery(&uploadPart) != nil { return } if !s.Storage.Exists(StorageTmp, upload.Id) { c.AbortWithError(http.StatusNotFound, ErrUploadNotExists) return } f, err := os.Create( s.Storage.Join(StorageTmp, upload.Id, fmt.Sprint(uploadPart.Part)), ) if err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } defer f.Close() defer c.Request.Body.Close() sha := sha256.New() t := io.TeeReader(c.Request.Body, sha) w, err := io.Copy(f, t) if err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } c.JSON(http.StatusCreated, gin.H{ "upload_id": upload.Id, "part": uploadPart.Part, "size": w, "sha256": hex.EncodeToString(sha.Sum(nil)), "status": "success", }) } func (s *Service) UploadCancel(c *gin.Context) { var upload UploadUri if c.BindUri(&upload) != nil { return } if err := s.Storage.Delete(StorageTmp, upload.Id); err != nil { c.AbortWithError(http.StatusNotFound, ErrUploadNotExists) return } c.JSON(http.StatusOK, gin.H{ "status": "success", }) } type UploadCompleteRequest struct { Sha256 string `json:"sha256" binding:"required,sha256"` Name string `json:"name" binding:"required"` Parts uint `json:"parts" binding:"required"` } func (s *Service) UploadComplete(c *gin.Context) { var uploadCompleteRequest UploadCompleteRequest if c.BindJSON(&uploadCompleteRequest) != nil { return } c.JSON(http.StatusOK, gin.H{ "sha256": uploadCompleteRequest.Sha256, "parts": uploadCompleteRequest.Parts, "name": uploadCompleteRequest.Name, }) } func (s *Service) UploadInit() { upload := s.Gin.Group("/upload") upload.GET("/create", s.UploadCreate) upload.POST("/part/:upload_id", s.UploadPart) upload.GET("/cancel/:upload_id", s.UploadCancel) upload.POST("/complete/:upload_id", s.UploadComplete) }