You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

object.go 5.6 kB

2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. package http
  2. import (
  3. "io"
  4. "mime/multipart"
  5. "net/http"
  6. "time"
  7. "github.com/gin-gonic/gin"
  8. "gitlink.org.cn/cloudream/common/consts/errorcode"
  9. "gitlink.org.cn/cloudream/common/models"
  10. "gitlink.org.cn/cloudream/common/pkgs/logger"
  11. myio "gitlink.org.cn/cloudream/common/utils/io"
  12. "gitlink.org.cn/cloudream/common/utils/serder"
  13. "gitlink.org.cn/cloudream/storage-client/internal/task"
  14. )
  15. type ObjectService struct {
  16. *Server
  17. }
  18. func (s *Server) ObjectSvc() *ObjectService {
  19. return &ObjectService{
  20. Server: s,
  21. }
  22. }
  23. type ObjectDownloadReq struct {
  24. UserID *int64 `form:"userID" binding:"required"`
  25. ObjectID *int64 `form:"objectID" binding:"required"`
  26. }
  27. func (s *ObjectService) Download(ctx *gin.Context) {
  28. log := logger.WithField("HTTP", "Object.Download")
  29. var req ObjectDownloadReq
  30. if err := ctx.ShouldBindQuery(&req); err != nil {
  31. log.Warnf("binding body: %s", err.Error())
  32. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  33. return
  34. }
  35. file, err := s.svc.ObjectSvc().DownloadObject(*req.UserID, *req.ObjectID)
  36. if err != nil {
  37. log.Warnf("downloading object: %s", err.Error())
  38. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "download object failed"))
  39. return
  40. }
  41. ctx.Writer.WriteHeader(http.StatusOK)
  42. // TODO 需要设置FileName
  43. ctx.Header("Content-Disposition", "attachment; filename=filename")
  44. ctx.Header("Content-Type", "application/octet-stream")
  45. buf := make([]byte, 4096)
  46. ctx.Stream(func(w io.Writer) bool {
  47. rd, err := file.Read(buf)
  48. if err == io.EOF {
  49. return false
  50. }
  51. if err != nil {
  52. log.Warnf("reading file data: %s", err.Error())
  53. return false
  54. }
  55. err = myio.WriteAll(w, buf[:rd])
  56. if err != nil {
  57. log.Warnf("writing data to response: %s", err.Error())
  58. return false
  59. }
  60. return true
  61. })
  62. }
  63. type ObjectUploadReq struct {
  64. Info ObjectUploadInfo `form:"info" binding:"required"`
  65. File *multipart.FileHeader `form:"file"`
  66. }
  67. type ObjectUploadInfo struct {
  68. UserID *int64 `json:"userID" binding:"required"`
  69. BucketID *int64 `json:"bucketID" binding:"required"`
  70. FileSize *int64 `json:"fileSize" binding:"required"`
  71. ObjectName string `json:"objectName" binding:"required"`
  72. Redundancy struct {
  73. Type string `json:"type" binding:"required"`
  74. Config any `json:"config" binding:"required"`
  75. } `json:"redundancy" binding:"required"`
  76. }
  77. type ObjectUploadResp struct {
  78. ObjectID int64 `json:"objectID,string"`
  79. }
  80. func (s *ObjectService) Upload(ctx *gin.Context) {
  81. log := logger.WithField("HTTP", "Object.Upload")
  82. var req ObjectUploadReq
  83. if err := ctx.ShouldBind(&req); err != nil {
  84. log.Warnf("binding body: %s", err.Error())
  85. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  86. return
  87. }
  88. switch req.Info.Redundancy.Type {
  89. case models.RedundancyRep:
  90. s.uploadRep(ctx, &req)
  91. return
  92. case models.RedundancyEC:
  93. }
  94. ctx.JSON(http.StatusForbidden, Failed(errorcode.OperationFailed, "not supported yet"))
  95. }
  96. func (s *ObjectService) uploadRep(ctx *gin.Context, req *ObjectUploadReq) {
  97. log := logger.WithField("HTTP", "Object.Upload")
  98. var repInfo models.RepRedundancyInfo
  99. if err := serder.AnyToAny(req.Info.Redundancy.Config, &repInfo); err != nil {
  100. log.Warnf("parsing rep redundancy config: %s", err.Error())
  101. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "invalid rep redundancy config"))
  102. return
  103. }
  104. file, err := req.File.Open()
  105. if err != nil {
  106. log.Warnf("opening file: %s", err.Error())
  107. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "open file failed"))
  108. return
  109. }
  110. taskID, err := s.svc.ObjectSvc().StartUploadingRepObjects(*req.Info.UserID, *req.Info.BucketID, []task.UploadObject{{
  111. ObjectName: req.Info.ObjectName,
  112. File: file,
  113. FileSize: *req.Info.FileSize,
  114. }}, repInfo.RepCount)
  115. if err != nil {
  116. log.Warnf("start uploading rep object task: %s", err.Error())
  117. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "start uploading task failed"))
  118. return
  119. }
  120. for {
  121. complete, uploadObjectResult, err := s.svc.ObjectSvc().WaitUploadingRepObjects(taskID, time.Second*5)
  122. if complete {
  123. if err != nil {
  124. log.Warnf("uploading rep object: %s", err.Error())
  125. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "uploading rep object failed"))
  126. return
  127. }
  128. uploadRet := uploadObjectResult.Results[0]
  129. if uploadRet.Error != nil {
  130. log.Warnf("uploading rep object: %s", uploadRet.Error)
  131. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, uploadRet.Error.Error()))
  132. return
  133. }
  134. ctx.JSON(http.StatusOK, OK(ObjectUploadResp{
  135. ObjectID: uploadRet.ObjectID,
  136. }))
  137. return
  138. }
  139. if err != nil {
  140. log.Warnf("waiting task: %s", err.Error())
  141. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "wait uploading task failed"))
  142. return
  143. }
  144. }
  145. }
  146. type ObjectDeleteReq struct {
  147. UserID *int64 `json:"userID" binding:"required"`
  148. ObjectID *int64 `json:"objectID" binding:"required"`
  149. }
  150. func (s *ObjectService) Delete(ctx *gin.Context) {
  151. log := logger.WithField("HTTP", "Object.Delete")
  152. var req ObjectDeleteReq
  153. if err := ctx.ShouldBindJSON(&req); err != nil {
  154. log.Warnf("binding body: %s", err.Error())
  155. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  156. return
  157. }
  158. err := s.svc.ObjectSvc().DeleteObject(*req.UserID, *req.ObjectID)
  159. if err != nil {
  160. log.Warnf("deleting object: %s", err.Error())
  161. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "delete object failed"))
  162. return
  163. }
  164. ctx.JSON(http.StatusOK, OK(nil))
  165. }

本项目旨在将云际存储公共基础设施化,使个人及企业可低门槛使用高效的云际存储服务(安装开箱即用云际存储客户端即可,无需关注其他组件的部署),同时支持用户灵活便捷定制云际存储的功能细节。