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 13 kB

10 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. package http
  2. import (
  3. "fmt"
  4. "io"
  5. "mime/multipart"
  6. "net/http"
  7. "net/url"
  8. "path"
  9. "path/filepath"
  10. "github.com/gin-gonic/gin"
  11. "gitlink.org.cn/cloudream/common/consts/errorcode"
  12. "gitlink.org.cn/cloudream/common/pkgs/logger"
  13. cdssdk "gitlink.org.cn/cloudream/common/sdks/storage"
  14. "gitlink.org.cn/cloudream/common/sdks/storage/cdsapi"
  15. "gitlink.org.cn/cloudream/common/utils/math2"
  16. "gitlink.org.cn/cloudream/storage/client/internal/config"
  17. "gitlink.org.cn/cloudream/storage/common/pkgs/downloader"
  18. )
  19. type ObjectService struct {
  20. *Server
  21. }
  22. func (s *Server) Object() *ObjectService {
  23. return &ObjectService{
  24. Server: s,
  25. }
  26. }
  27. func (s *ObjectService) ListByPath(ctx *gin.Context) {
  28. log := logger.WithField("HTTP", "Object.ListByPath")
  29. var req cdsapi.ObjectListByPath
  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. coms, objs, err := s.svc.ObjectSvc().GetByPath(req.UserID, req.PackageID, req.Path, req.IsPrefix, req.NoRecursive)
  36. if err != nil {
  37. log.Warnf("listing objects: %s", err.Error())
  38. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("listing objects: %v", err)))
  39. return
  40. }
  41. ctx.JSON(http.StatusOK, OK(cdsapi.ObjectListByPathResp{CommonPrefixes: coms, Objects: objs}))
  42. }
  43. func (s *ObjectService) ListByIDs(ctx *gin.Context) {
  44. log := logger.WithField("HTTP", "Object.ListByIDs")
  45. var req cdsapi.ObjectListByIDs
  46. if err := ctx.ShouldBindJSON(&req); err != nil {
  47. log.Warnf("binding body: %s", err.Error())
  48. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  49. return
  50. }
  51. objs, err := s.svc.ObjectSvc().GetByIDs(req.UserID, req.ObjectIDs)
  52. if err != nil {
  53. log.Warnf("listing objects: %s", err.Error())
  54. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("listing objects: %v", err)))
  55. return
  56. }
  57. ctx.JSON(http.StatusOK, OK(cdsapi.ObjectListByIDsResp{Objects: objs}))
  58. }
  59. type ObjectUploadReq struct {
  60. Info cdsapi.ObjectUploadInfo `form:"info" binding:"required"`
  61. Files []*multipart.FileHeader `form:"files"`
  62. }
  63. func (s *ObjectService) Upload(ctx *gin.Context) {
  64. log := logger.WithField("HTTP", "Object.Upload")
  65. var req ObjectUploadReq
  66. if err := ctx.ShouldBind(&req); err != nil {
  67. log.Warnf("binding body: %s", err.Error())
  68. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  69. return
  70. }
  71. up, err := s.svc.Uploader.BeginUpdate(req.Info.UserID, req.Info.PackageID, req.Info.Affinity, req.Info.LoadTo, req.Info.LoadToPath)
  72. if err != nil {
  73. log.Warnf("begin update: %s", err.Error())
  74. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("begin update: %v", err)))
  75. return
  76. }
  77. defer up.Abort()
  78. var pathes []string
  79. for _, file := range req.Files {
  80. f, err := file.Open()
  81. if err != nil {
  82. log.Warnf("open file: %s", err.Error())
  83. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("open file %v: %v", file.Filename, err)))
  84. return
  85. }
  86. path, err := url.PathUnescape(file.Filename)
  87. if err != nil {
  88. log.Warnf("unescape filename: %s", err.Error())
  89. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("unescape filename %v: %v", file.Filename, err)))
  90. return
  91. }
  92. path = filepath.ToSlash(path)
  93. err = up.Upload(path, file.Size, f)
  94. if err != nil {
  95. log.Warnf("uploading file: %s", err.Error())
  96. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("uploading file %v: %v", file.Filename, err)))
  97. return
  98. }
  99. pathes = append(pathes, path)
  100. }
  101. ret, err := up.Commit()
  102. if err != nil {
  103. log.Warnf("commit update: %s", err.Error())
  104. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("commit update: %v", err)))
  105. return
  106. }
  107. uploadeds := make([]cdssdk.Object, len(pathes))
  108. for i := range pathes {
  109. uploadeds[i] = ret.Objects[pathes[i]]
  110. }
  111. ctx.JSON(http.StatusOK, OK(cdsapi.ObjectUploadResp{Uploadeds: uploadeds}))
  112. }
  113. func (s *ObjectService) Download(ctx *gin.Context) {
  114. log := logger.WithField("HTTP", "Object.Download")
  115. var req cdsapi.ObjectDownload
  116. if err := ctx.ShouldBindQuery(&req); err != nil {
  117. log.Warnf("binding body: %s", err.Error())
  118. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  119. return
  120. }
  121. off := req.Offset
  122. len := int64(-1)
  123. if req.Length != nil {
  124. len = *req.Length
  125. }
  126. file, err := s.svc.ObjectSvc().Download(req.UserID, downloader.DownloadReqeust{
  127. ObjectID: req.ObjectID,
  128. Offset: off,
  129. Length: len,
  130. })
  131. if err != nil {
  132. log.Warnf("downloading object: %s", err.Error())
  133. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "download object failed"))
  134. return
  135. }
  136. if file.File == nil {
  137. log.Warnf("object not found: %d", req.ObjectID)
  138. ctx.JSON(http.StatusOK, Failed(errorcode.DataNotFound, "object not found"))
  139. return
  140. }
  141. defer file.File.Close()
  142. ctx.Header("Content-Disposition", "attachment; filename="+url.PathEscape(path.Base(file.Object.Path)))
  143. ctx.Header("Content-Type", "application/octet-stream")
  144. ctx.Header("Content-Transfer-Encoding", "binary")
  145. n, err := io.Copy(ctx.Writer, file.File)
  146. if err != nil {
  147. log.Warnf("copying file: %s", err.Error())
  148. }
  149. // TODO 当client不在某个代理节点上时如何处理?
  150. if config.Cfg().StorageID > 0 {
  151. s.svc.AccessStat.AddAccessCounter(file.Object.ObjectID, file.Object.PackageID, config.Cfg().StorageID, math2.DivOrDefault(float64(n), float64(file.Object.Size), 1))
  152. }
  153. }
  154. func (s *ObjectService) DownloadByPath(ctx *gin.Context) {
  155. log := logger.WithField("HTTP", "Object.DownloadByPath")
  156. var req cdsapi.ObjectDownloadByPath
  157. if err := ctx.ShouldBindQuery(&req); err != nil {
  158. log.Warnf("binding body: %s", err.Error())
  159. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  160. return
  161. }
  162. _, obj, err := s.svc.ObjectSvc().GetByPath(req.UserID, req.PackageID, req.Path, false, false)
  163. if err != nil {
  164. log.Warnf("getting object by path: %s", err.Error())
  165. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "get object by path failed"))
  166. return
  167. }
  168. if len(obj) == 0 {
  169. log.Warnf("object not found: %s", req.Path)
  170. ctx.JSON(http.StatusOK, Failed(errorcode.DataNotFound, "object not found"))
  171. return
  172. }
  173. off := req.Offset
  174. len := int64(-1)
  175. if req.Length != nil {
  176. len = *req.Length
  177. }
  178. file, err := s.svc.ObjectSvc().Download(req.UserID, downloader.DownloadReqeust{
  179. ObjectID: obj[0].ObjectID,
  180. Offset: off,
  181. Length: len,
  182. })
  183. if err != nil {
  184. log.Warnf("downloading object: %s", err.Error())
  185. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "download object failed"))
  186. return
  187. }
  188. defer file.File.Close()
  189. ctx.Header("Content-Disposition", "attachment; filename="+url.PathEscape(path.Base(file.Object.Path)))
  190. ctx.Header("Content-Type", "application/octet-stream")
  191. ctx.Header("Content-Transfer-Encoding", "binary")
  192. n, err := io.Copy(ctx.Writer, file.File)
  193. if err != nil {
  194. log.Warnf("copying file: %s", err.Error())
  195. }
  196. if config.Cfg().StorageID > 0 {
  197. s.svc.AccessStat.AddAccessCounter(file.Object.ObjectID, file.Object.PackageID, config.Cfg().StorageID, math2.DivOrDefault(float64(n), float64(file.Object.Size), 1))
  198. }
  199. }
  200. func (s *ObjectService) UpdateInfo(ctx *gin.Context) {
  201. log := logger.WithField("HTTP", "Object.UpdateInfo")
  202. var req cdsapi.ObjectUpdateInfo
  203. if err := ctx.ShouldBindJSON(&req); err != nil {
  204. log.Warnf("binding body: %s", err.Error())
  205. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  206. return
  207. }
  208. sucs, err := s.svc.ObjectSvc().UpdateInfo(req.UserID, req.Updatings)
  209. if err != nil {
  210. log.Warnf("updating objects: %s", err.Error())
  211. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "update objects failed"))
  212. return
  213. }
  214. ctx.JSON(http.StatusOK, OK(cdsapi.ObjectUpdateInfoResp{Successes: sucs}))
  215. }
  216. func (s *ObjectService) UpdateInfoByPath(ctx *gin.Context) {
  217. log := logger.WithField("HTTP", "Object.UpdateInfoByPath")
  218. var req cdsapi.ObjectUpdateInfoByPath
  219. if err := ctx.ShouldBindJSON(&req); err != nil {
  220. log.Warnf("binding body: %s", err.Error())
  221. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  222. return
  223. }
  224. _, obj, err := s.svc.ObjectSvc().GetByPath(req.UserID, req.PackageID, req.Path, true, false)
  225. if err != nil {
  226. log.Warnf("getting object by path: %s", err.Error())
  227. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "get object by path failed"))
  228. return
  229. }
  230. if len(obj) == 0 {
  231. log.Warnf("object not found: %s", req.Path)
  232. ctx.JSON(http.StatusOK, Failed(errorcode.DataNotFound, "object not found"))
  233. return
  234. }
  235. sucs, err := s.svc.ObjectSvc().UpdateInfo(req.UserID, []cdsapi.UpdatingObject{{
  236. ObjectID: obj[0].ObjectID,
  237. UpdateTime: req.UpdateTime,
  238. }})
  239. if err != nil {
  240. log.Warnf("updating objects: %s", err.Error())
  241. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "update objects failed"))
  242. return
  243. }
  244. if len(sucs) == 0 {
  245. }
  246. ctx.JSON(http.StatusOK, OK(cdsapi.ObjectUpdateInfoByPathResp{}))
  247. }
  248. func (s *ObjectService) Move(ctx *gin.Context) {
  249. log := logger.WithField("HTTP", "Object.Move")
  250. var req cdsapi.ObjectMove
  251. if err := ctx.ShouldBindJSON(&req); err != nil {
  252. log.Warnf("binding body: %s", err.Error())
  253. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  254. return
  255. }
  256. sucs, err := s.svc.ObjectSvc().Move(req.UserID, req.Movings)
  257. if err != nil {
  258. log.Warnf("moving objects: %s", err.Error())
  259. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "move objects failed"))
  260. return
  261. }
  262. ctx.JSON(http.StatusOK, OK(cdsapi.ObjectMoveResp{Successes: sucs}))
  263. }
  264. func (s *ObjectService) Delete(ctx *gin.Context) {
  265. log := logger.WithField("HTTP", "Object.Delete")
  266. var req cdsapi.ObjectDelete
  267. if err := ctx.ShouldBindJSON(&req); err != nil {
  268. log.Warnf("binding body: %s", err.Error())
  269. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  270. return
  271. }
  272. err := s.svc.ObjectSvc().Delete(req.UserID, req.ObjectIDs)
  273. if err != nil {
  274. log.Warnf("deleting objects: %s", err.Error())
  275. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "delete objects failed"))
  276. return
  277. }
  278. ctx.JSON(http.StatusOK, OK(nil))
  279. }
  280. func (s *ObjectService) DeleteByPath(ctx *gin.Context) {
  281. log := logger.WithField("HTTP", "Object.DeleteByPath")
  282. var req cdsapi.ObjectDeleteByPath
  283. if err := ctx.ShouldBindJSON(&req); err != nil {
  284. log.Warnf("binding body: %s", err.Error())
  285. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  286. return
  287. }
  288. _, obj, err := s.svc.ObjectSvc().GetByPath(req.UserID, req.PackageID, req.Path, false, false)
  289. if err != nil {
  290. log.Warnf("getting object by path: %s", err.Error())
  291. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "get object by path failed"))
  292. return
  293. }
  294. if len(obj) == 0 {
  295. ctx.JSON(http.StatusOK, OK(nil))
  296. return
  297. }
  298. err = s.svc.ObjectSvc().Delete(req.UserID, []cdssdk.ObjectID{obj[0].ObjectID})
  299. if err != nil {
  300. log.Warnf("deleting objects: %s", err.Error())
  301. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "delete objects failed"))
  302. return
  303. }
  304. ctx.JSON(http.StatusOK, OK(nil))
  305. }
  306. func (s *ObjectService) Clone(ctx *gin.Context) {
  307. log := logger.WithField("HTTP", "Object.Clone")
  308. var req cdsapi.ObjectClone
  309. if err := ctx.ShouldBindJSON(&req); err != nil {
  310. log.Warnf("binding body: %s", err.Error())
  311. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  312. return
  313. }
  314. objs, err := s.svc.ObjectSvc().Clone(req.UserID, req.Clonings)
  315. if err != nil {
  316. log.Warnf("cloning object: %s", err.Error())
  317. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "clone object failed"))
  318. return
  319. }
  320. ctx.JSON(http.StatusOK, OK(cdsapi.ObjectCloneResp{Objects: objs}))
  321. }
  322. func (s *ObjectService) GetPackageObjects(ctx *gin.Context) {
  323. log := logger.WithField("HTTP", "Object.GetPackageObjects")
  324. var req cdsapi.ObjectGetPackageObjects
  325. if err := ctx.ShouldBindQuery(&req); err != nil {
  326. log.Warnf("binding body: %s", err.Error())
  327. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  328. return
  329. }
  330. objs, err := s.svc.ObjectSvc().GetPackageObjects(req.UserID, req.PackageID)
  331. if err != nil {
  332. log.Warnf("getting package objects: %s", err.Error())
  333. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "get package object failed"))
  334. return
  335. }
  336. ctx.JSON(http.StatusOK, OK(cdsapi.ObjectGetPackageObjectsResp{Objects: objs}))
  337. }

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