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 15 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
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  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. resp, err := s.svc.ObjectSvc().GetByPath(req)
  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(resp))
  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.ShouldBindQuery(&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, 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. resp, err := s.svc.ObjectSvc().GetByPath(cdsapi.ObjectListByPath{
  163. UserID: req.UserID, PackageID: req.PackageID, Path: req.Path,
  164. })
  165. if err != nil {
  166. log.Warnf("getting object by path: %s", err.Error())
  167. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "get object by path failed"))
  168. return
  169. }
  170. if len(resp.Objects) == 0 {
  171. log.Warnf("object not found: %s", req.Path)
  172. ctx.JSON(http.StatusOK, Failed(errorcode.DataNotFound, "object not found"))
  173. return
  174. }
  175. off := req.Offset
  176. len := int64(-1)
  177. if req.Length != nil {
  178. len = *req.Length
  179. }
  180. file, err := s.svc.ObjectSvc().Download(req.UserID, downloader.DownloadReqeust{
  181. ObjectID: resp.Objects[0].ObjectID,
  182. Offset: off,
  183. Length: len,
  184. })
  185. if err != nil {
  186. log.Warnf("downloading object: %s", err.Error())
  187. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "download object failed"))
  188. return
  189. }
  190. defer file.File.Close()
  191. ctx.Header("Content-Disposition", "attachment; filename="+url.PathEscape(path.Base(file.Object.Path)))
  192. ctx.Header("Content-Type", "application/octet-stream")
  193. ctx.Header("Content-Transfer-Encoding", "binary")
  194. n, err := io.Copy(ctx.Writer, file.File)
  195. if err != nil {
  196. log.Warnf("copying file: %s", err.Error())
  197. }
  198. if config.Cfg().StorageID > 0 {
  199. s.svc.AccessStat.AddAccessCounter(file.Object.ObjectID, file.Object.PackageID, config.Cfg().StorageID, math2.DivOrDefault(float64(n), float64(file.Object.Size), 1))
  200. }
  201. }
  202. func (s *ObjectService) UpdateInfo(ctx *gin.Context) {
  203. log := logger.WithField("HTTP", "Object.UpdateInfo")
  204. var req cdsapi.ObjectUpdateInfo
  205. if err := ctx.ShouldBindJSON(&req); err != nil {
  206. log.Warnf("binding body: %s", err.Error())
  207. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  208. return
  209. }
  210. sucs, err := s.svc.ObjectSvc().UpdateInfo(req.UserID, req.Updatings)
  211. if err != nil {
  212. log.Warnf("updating objects: %s", err.Error())
  213. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "update objects failed"))
  214. return
  215. }
  216. ctx.JSON(http.StatusOK, OK(cdsapi.ObjectUpdateInfoResp{Successes: sucs}))
  217. }
  218. func (s *ObjectService) UpdateInfoByPath(ctx *gin.Context) {
  219. log := logger.WithField("HTTP", "Object.UpdateInfoByPath")
  220. var req cdsapi.ObjectUpdateInfoByPath
  221. if err := ctx.ShouldBindJSON(&req); err != nil {
  222. log.Warnf("binding body: %s", err.Error())
  223. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  224. return
  225. }
  226. resp, err := s.svc.ObjectSvc().GetByPath(cdsapi.ObjectListByPath{
  227. UserID: req.UserID, PackageID: req.PackageID, Path: req.Path,
  228. })
  229. if err != nil {
  230. log.Warnf("getting object by path: %s", err.Error())
  231. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "get object by path failed"))
  232. return
  233. }
  234. if len(resp.Objects) == 0 {
  235. log.Warnf("object not found: %s", req.Path)
  236. ctx.JSON(http.StatusOK, Failed(errorcode.DataNotFound, "object not found"))
  237. return
  238. }
  239. sucs, err := s.svc.ObjectSvc().UpdateInfo(req.UserID, []cdsapi.UpdatingObject{{
  240. ObjectID: resp.Objects[0].ObjectID,
  241. UpdateTime: req.UpdateTime,
  242. }})
  243. if err != nil {
  244. log.Warnf("updating objects: %s", err.Error())
  245. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "update objects failed"))
  246. return
  247. }
  248. if len(sucs) == 0 {
  249. }
  250. ctx.JSON(http.StatusOK, OK(cdsapi.ObjectUpdateInfoByPathResp{}))
  251. }
  252. func (s *ObjectService) Move(ctx *gin.Context) {
  253. log := logger.WithField("HTTP", "Object.Move")
  254. var req cdsapi.ObjectMove
  255. if err := ctx.ShouldBindJSON(&req); err != nil {
  256. log.Warnf("binding body: %s", err.Error())
  257. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  258. return
  259. }
  260. sucs, err := s.svc.ObjectSvc().Move(req.UserID, req.Movings)
  261. if err != nil {
  262. log.Warnf("moving objects: %s", err.Error())
  263. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "move objects failed"))
  264. return
  265. }
  266. ctx.JSON(http.StatusOK, OK(cdsapi.ObjectMoveResp{Successes: sucs}))
  267. }
  268. func (s *ObjectService) Delete(ctx *gin.Context) {
  269. log := logger.WithField("HTTP", "Object.Delete")
  270. var req cdsapi.ObjectDelete
  271. if err := ctx.ShouldBindJSON(&req); err != nil {
  272. log.Warnf("binding body: %s", err.Error())
  273. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  274. return
  275. }
  276. err := s.svc.ObjectSvc().Delete(req.UserID, req.ObjectIDs)
  277. if err != nil {
  278. log.Warnf("deleting objects: %s", err.Error())
  279. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "delete objects failed"))
  280. return
  281. }
  282. ctx.JSON(http.StatusOK, OK(nil))
  283. }
  284. func (s *ObjectService) DeleteByPath(ctx *gin.Context) {
  285. log := logger.WithField("HTTP", "Object.DeleteByPath")
  286. var req cdsapi.ObjectDeleteByPath
  287. if err := ctx.ShouldBindJSON(&req); err != nil {
  288. log.Warnf("binding body: %s", err.Error())
  289. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  290. return
  291. }
  292. resp, err := s.svc.ObjectSvc().GetByPath(cdsapi.ObjectListByPath{
  293. UserID: req.UserID, PackageID: req.PackageID, Path: req.Path,
  294. })
  295. if err != nil {
  296. log.Warnf("getting object by path: %s", err.Error())
  297. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "get object by path failed"))
  298. return
  299. }
  300. if len(resp.Objects) == 0 {
  301. ctx.JSON(http.StatusOK, OK(nil))
  302. return
  303. }
  304. err = s.svc.ObjectSvc().Delete(req.UserID, []cdssdk.ObjectID{resp.Objects[0].ObjectID})
  305. if err != nil {
  306. log.Warnf("deleting objects: %s", err.Error())
  307. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "delete objects failed"))
  308. return
  309. }
  310. ctx.JSON(http.StatusOK, OK(nil))
  311. }
  312. func (s *ObjectService) Clone(ctx *gin.Context) {
  313. log := logger.WithField("HTTP", "Object.Clone")
  314. var req cdsapi.ObjectClone
  315. if err := ctx.ShouldBindJSON(&req); err != nil {
  316. log.Warnf("binding body: %s", err.Error())
  317. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  318. return
  319. }
  320. objs, err := s.svc.ObjectSvc().Clone(req.UserID, req.Clonings)
  321. if err != nil {
  322. log.Warnf("cloning object: %s", err.Error())
  323. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "clone object failed"))
  324. return
  325. }
  326. ctx.JSON(http.StatusOK, OK(cdsapi.ObjectCloneResp{Objects: objs}))
  327. }
  328. func (s *ObjectService) GetPackageObjects(ctx *gin.Context) {
  329. log := logger.WithField("HTTP", "Object.GetPackageObjects")
  330. var req cdsapi.ObjectGetPackageObjects
  331. if err := ctx.ShouldBindQuery(&req); err != nil {
  332. log.Warnf("binding body: %s", err.Error())
  333. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  334. return
  335. }
  336. objs, err := s.svc.ObjectSvc().GetPackageObjects(req.UserID, req.PackageID)
  337. if err != nil {
  338. log.Warnf("getting package objects: %s", err.Error())
  339. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "get package object failed"))
  340. return
  341. }
  342. ctx.JSON(http.StatusOK, OK(cdsapi.ObjectGetPackageObjectsResp{Objects: objs}))
  343. }
  344. func (s *ObjectService) NewMultipartUpload(ctx *gin.Context) {
  345. log := logger.WithField("HTTP", "Object.NewMultipartUpload")
  346. var req cdsapi.ObjectNewMultipartUpload
  347. if err := ctx.ShouldBindJSON(&req); err != nil {
  348. log.Warnf("binding body: %s", err.Error())
  349. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  350. return
  351. }
  352. obj, err := s.svc.ObjectSvc().NewMultipartUploadObject(req.UserID, req.PackageID, req.Path)
  353. if err != nil {
  354. log.Warnf("new multipart upload object: %s", err.Error())
  355. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "new multipart upload object failed"))
  356. return
  357. }
  358. ctx.JSON(http.StatusOK, OK(cdsapi.ObjectNewMultipartUploadResp{Object: obj}))
  359. }
  360. type ObjectUploadPartReq struct {
  361. Info cdsapi.ObjectUploadPartInfo `form:"info" binding:"required"`
  362. File *multipart.FileHeader `form:"file"`
  363. }
  364. func (s *ObjectService) UploadPart(ctx *gin.Context) {
  365. log := logger.WithField("HTTP", "Object.UploadPart")
  366. var req ObjectUploadPartReq
  367. if err := ctx.ShouldBind(&req); err != nil {
  368. log.Warnf("binding body: %s", err.Error())
  369. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  370. return
  371. }
  372. file, err := req.File.Open()
  373. if err != nil {
  374. log.Warnf("open file: %s", err.Error())
  375. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "open file failed"))
  376. return
  377. }
  378. defer file.Close()
  379. err = s.svc.Uploader.UploadPart(req.Info.UserID, req.Info.ObjectID, req.Info.Index, file)
  380. if err != nil {
  381. log.Warnf("uploading part: %s", err.Error())
  382. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("upload part: %v", err)))
  383. return
  384. }
  385. ctx.JSON(http.StatusOK, OK(cdsapi.ObjectUploadPartResp{}))
  386. }
  387. func (s *ObjectService) CompleteMultipartUpload(ctx *gin.Context) {
  388. log := logger.WithField("HTTP", "Object.CompleteMultipartUpload")
  389. var req cdsapi.ObjectCompleteMultipartUpload
  390. if err := ctx.ShouldBindJSON(&req); err != nil {
  391. log.Warnf("binding body: %s", err.Error())
  392. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  393. return
  394. }
  395. obj, err := s.svc.ObjectSvc().CompleteMultipartUpload(req.UserID, req.ObjectID, req.Indexes)
  396. if err != nil {
  397. log.Warnf("completing multipart upload: %s", err.Error())
  398. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("complete multipart upload: %v", err)))
  399. return
  400. }
  401. ctx.JSON(http.StatusOK, OK(cdsapi.ObjectCompleteMultipartUploadResp{Object: obj}))
  402. }

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