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

1 year ago
10 months ago
10 months ago
10 months ago
1 year 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
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. package cdsapi
  2. import (
  3. "fmt"
  4. "io"
  5. "mime"
  6. "net/http"
  7. "net/url"
  8. "strings"
  9. "time"
  10. "gitlink.org.cn/cloudream/common/consts/errorcode"
  11. "gitlink.org.cn/cloudream/common/pkgs/iterator"
  12. "gitlink.org.cn/cloudream/common/sdks"
  13. cdssdk "gitlink.org.cn/cloudream/common/sdks/storage"
  14. "gitlink.org.cn/cloudream/common/utils/http2"
  15. "gitlink.org.cn/cloudream/common/utils/serder"
  16. )
  17. type ObjectService struct {
  18. *Client
  19. }
  20. func (c *Client) Object() *ObjectService {
  21. return &ObjectService{
  22. Client: c,
  23. }
  24. }
  25. const ObjectListPathByPath = "/object/listByPath"
  26. type ObjectListByPath struct {
  27. UserID cdssdk.UserID `form:"userID" binding:"required" url:"userID"`
  28. PackageID cdssdk.PackageID `form:"packageID" binding:"required" url:"packageID"`
  29. Path string `form:"path" url:"path"` // 允许为空字符串
  30. IsPrefix bool `form:"isPrefix" url:"isPrefix"`
  31. NoRecursive bool `form:"noRecursive" url:"noRecursive"` // 仅当isPrefix为true时有效,表示仅查询直接属于Prefix下的对象,对于更深的对象,返回它们的公共前缀
  32. }
  33. func (r *ObjectListByPath) MakeParam() *sdks.RequestParam {
  34. return sdks.MakeQueryParam(http.MethodGet, ObjectListPathByPath, r)
  35. }
  36. type ObjectListByPathResp struct {
  37. CommonPrefixes []string `json:"commonPrefixes"` // 仅在IsPrefix为true且NoRecursive为true时有效,包含更深层对象的shared prefix
  38. Objects []cdssdk.Object `json:"objects"` // 如果IsPrefix为true且NoRecursive为false,则返回所有匹配的对象,否则只返回直接属于Prefix下的对象
  39. }
  40. func (r *ObjectListByPathResp) ParseResponse(resp *http.Response) error {
  41. return sdks.ParseCodeDataJSONResponse(resp, r)
  42. }
  43. func (c *ObjectService) ListByPath(req ObjectListByPath) (*ObjectListByPathResp, error) {
  44. return JSONAPI[*ObjectListByPathResp](c.cfg, http.DefaultClient, &req)
  45. }
  46. const ObjectListByIDsPath = "/object/listByIDs"
  47. type ObjectListByIDs struct {
  48. UserID cdssdk.UserID `form:"userID" binding:"required" url:"userID"`
  49. ObjectIDs []cdssdk.ObjectID `form:"objectIDs" binding:"required" url:"objectIDs"`
  50. }
  51. func (r *ObjectListByIDs) MakeParam() *sdks.RequestParam {
  52. return sdks.MakeQueryParam(http.MethodGet, ObjectListByIDsPath, r)
  53. }
  54. type ObjectListByIDsResp struct {
  55. Objects []*cdssdk.Object `json:"object"` // 与ObjectIDs一一对应,如果某个ID不存在,则对应位置为nil
  56. }
  57. func (r *ObjectListByIDsResp) ParseResponse(resp *http.Response) error {
  58. return sdks.ParseCodeDataJSONResponse(resp, r)
  59. }
  60. func (c *ObjectService) ListByIDs(req ObjectListByIDs) (*ObjectListByIDsResp, error) {
  61. return JSONAPI[*ObjectListByIDsResp](c.cfg, http.DefaultClient, &req)
  62. }
  63. const ObjectUploadPath = "/object/upload"
  64. type ObjectUpload struct {
  65. ObjectUploadInfo
  66. Files UploadObjectIterator `json:"-"`
  67. }
  68. type ObjectUploadInfo struct {
  69. UserID cdssdk.UserID `json:"userID" binding:"required"`
  70. PackageID cdssdk.PackageID `json:"packageID" binding:"required"`
  71. Affinity cdssdk.StorageID `json:"affinity"`
  72. LoadTo []cdssdk.StorageID `json:"loadTo"`
  73. LoadToPath []string `json:"loadToPath"`
  74. }
  75. type UploadingObject struct {
  76. Path string
  77. File io.ReadCloser
  78. }
  79. type UploadObjectIterator = iterator.Iterator[*UploadingObject]
  80. type ObjectUploadResp struct {
  81. Uploadeds []cdssdk.Object `json:"uploadeds"`
  82. }
  83. func (c *ObjectService) Upload(req ObjectUpload) (*ObjectUploadResp, error) {
  84. url, err := url.JoinPath(c.cfg.URL, ObjectUploadPath)
  85. if err != nil {
  86. return nil, err
  87. }
  88. infoJSON, err := serder.ObjectToJSON(req)
  89. if err != nil {
  90. return nil, fmt.Errorf("upload info to json: %w", err)
  91. }
  92. resp, err := http2.PostMultiPart(url, http2.MultiPartRequestParam{
  93. Form: map[string]string{"info": string(infoJSON)},
  94. Files: iterator.Map(req.Files, func(src *UploadingObject) (*http2.IterMultiPartFile, error) {
  95. return &http2.IterMultiPartFile{
  96. FieldName: "files",
  97. FileName: src.Path,
  98. File: src.File,
  99. }, nil
  100. }),
  101. })
  102. if err != nil {
  103. return nil, err
  104. }
  105. contType := resp.Header.Get("Content-Type")
  106. if strings.Contains(contType, http2.ContentTypeJSON) {
  107. var err error
  108. var codeResp response[ObjectUploadResp]
  109. if codeResp, err = serder.JSONToObjectStreamEx[response[ObjectUploadResp]](resp.Body); err != nil {
  110. return nil, fmt.Errorf("parsing response: %w", err)
  111. }
  112. if codeResp.Code == errorcode.OK {
  113. return &codeResp.Data, nil
  114. }
  115. return nil, codeResp.ToError()
  116. }
  117. return nil, fmt.Errorf("unknow response content type: %s", contType)
  118. }
  119. const ObjectDownloadPath = "/object/download"
  120. type ObjectDownload struct {
  121. UserID cdssdk.UserID `form:"userID" json:"userID" binding:"required"`
  122. ObjectID cdssdk.ObjectID `form:"objectID" json:"objectID" binding:"required"`
  123. Offset int64 `form:"offset" json:"offset,omitempty"`
  124. Length *int64 `form:"length" json:"length,omitempty"`
  125. }
  126. type DownloadingObject struct {
  127. Path string
  128. File io.ReadCloser
  129. }
  130. func (c *ObjectService) Download(req ObjectDownload) (*DownloadingObject, error) {
  131. url, err := url.JoinPath(c.cfg.URL, ObjectDownloadPath)
  132. if err != nil {
  133. return nil, err
  134. }
  135. resp, err := http2.GetJSON(url, http2.RequestParam{
  136. Query: req,
  137. })
  138. if err != nil {
  139. return nil, err
  140. }
  141. contType := resp.Header.Get("Content-Type")
  142. if strings.Contains(contType, http2.ContentTypeJSON) {
  143. var codeResp response[any]
  144. if err := serder.JSONToObjectStream(resp.Body, &codeResp); err != nil {
  145. return nil, fmt.Errorf("parsing response: %w", err)
  146. }
  147. return nil, codeResp.ToError()
  148. }
  149. _, params, err := mime.ParseMediaType(resp.Header.Get("Content-Disposition"))
  150. if err != nil {
  151. return nil, fmt.Errorf("parsing content disposition: %w", err)
  152. }
  153. return &DownloadingObject{
  154. Path: params["filename"],
  155. File: resp.Body,
  156. }, nil
  157. }
  158. const ObjectDownloadByPathPath = "/object/downloadByPath"
  159. type ObjectDownloadByPath struct {
  160. UserID cdssdk.UserID `form:"userID" json:"userID" binding:"required"`
  161. PackageID cdssdk.PackageID `form:"packageID" json:"packageID" binding:"required"`
  162. Path string `form:"path" json:"path" binding:"required"`
  163. Offset int64 `form:"offset" json:"offset,omitempty"`
  164. Length *int64 `form:"length" json:"length,omitempty"`
  165. }
  166. func (c *ObjectService) DownloadByPath(req ObjectDownloadByPath) (*DownloadingObject, error) {
  167. url, err := url.JoinPath(c.cfg.URL, ObjectDownloadByPathPath)
  168. if err != nil {
  169. return nil, err
  170. }
  171. resp, err := http2.GetJSON(url, http2.RequestParam{
  172. Query: req,
  173. })
  174. if err != nil {
  175. return nil, err
  176. }
  177. contType := resp.Header.Get("Content-Type")
  178. if strings.Contains(contType, http2.ContentTypeJSON) {
  179. var codeResp response[any]
  180. if err := serder.JSONToObjectStream(resp.Body, &codeResp); err != nil {
  181. return nil, fmt.Errorf("parsing response: %w", err)
  182. }
  183. return nil, codeResp.ToError()
  184. }
  185. _, params, err := mime.ParseMediaType(resp.Header.Get("Content-Disposition"))
  186. if err != nil {
  187. return nil, fmt.Errorf("parsing content disposition: %w", err)
  188. }
  189. return &DownloadingObject{
  190. Path: params["filename"],
  191. File: resp.Body,
  192. }, nil
  193. }
  194. const ObjectUpdateInfoPath = "/object/updateInfo"
  195. type UpdatingObject struct {
  196. ObjectID cdssdk.ObjectID `json:"objectID" binding:"required"`
  197. UpdateTime time.Time `json:"updateTime" binding:"required"`
  198. }
  199. func (u *UpdatingObject) ApplyTo(obj *cdssdk.Object) {
  200. obj.UpdateTime = u.UpdateTime
  201. }
  202. type ObjectUpdateInfo struct {
  203. UserID cdssdk.UserID `json:"userID" binding:"required"`
  204. Updatings []UpdatingObject `json:"updatings" binding:"required"`
  205. }
  206. func (r *ObjectUpdateInfo) MakeParam() *sdks.RequestParam {
  207. return sdks.MakeJSONParam(http.MethodPost, ObjectUpdateInfoPath, r)
  208. }
  209. type ObjectUpdateInfoResp struct {
  210. Successes []cdssdk.ObjectID `json:"successes"`
  211. }
  212. func (r *ObjectUpdateInfoResp) ParseResponse(resp *http.Response) error {
  213. return sdks.ParseCodeDataJSONResponse(resp, r)
  214. }
  215. func (c *ObjectService) UpdateInfo(req ObjectUpdateInfo) (*ObjectUpdateInfoResp, error) {
  216. return JSONAPI[*ObjectUpdateInfoResp](c.cfg, http.DefaultClient, &req)
  217. }
  218. const ObjectUpdateInfoByPathPath = "/object/updateInfoByPath"
  219. type ObjectUpdateInfoByPath struct {
  220. UserID cdssdk.UserID `json:"userID" binding:"required"`
  221. PackageID cdssdk.PackageID `json:"packageID" binding:"required"`
  222. Path string `json:"path" binding:"required"`
  223. UpdateTime time.Time `json:"updateTime" binding:"required"`
  224. }
  225. func (r *ObjectUpdateInfoByPath) MakeParam() *sdks.RequestParam {
  226. return sdks.MakeJSONParam(http.MethodPost, ObjectUpdateInfoByPathPath, r)
  227. }
  228. type ObjectUpdateInfoByPathResp struct{}
  229. func (r *ObjectUpdateInfoByPathResp) ParseResponse(resp *http.Response) error {
  230. return sdks.ParseCodeDataJSONResponse(resp, r)
  231. }
  232. func (c *ObjectService) UpdateInfoByPath(req ObjectUpdateInfoByPath) (*ObjectUpdateInfoByPathResp, error) {
  233. return JSONAPI[*ObjectUpdateInfoByPathResp](c.cfg, http.DefaultClient, &req)
  234. }
  235. const ObjectMovePath = "/object/move"
  236. type MovingObject struct {
  237. ObjectID cdssdk.ObjectID `json:"objectID" binding:"required"`
  238. PackageID cdssdk.PackageID `json:"packageID" binding:"required"`
  239. Path string `json:"path" binding:"required"`
  240. }
  241. func (m *MovingObject) ApplyTo(obj *cdssdk.Object) {
  242. obj.PackageID = m.PackageID
  243. obj.Path = m.Path
  244. }
  245. type ObjectMove struct {
  246. UserID cdssdk.UserID `json:"userID" binding:"required"`
  247. Movings []MovingObject `json:"movings" binding:"required"`
  248. }
  249. func (r *ObjectMove) MakeParam() *sdks.RequestParam {
  250. return sdks.MakeJSONParam(http.MethodPost, ObjectMovePath, r)
  251. }
  252. type ObjectMoveResp struct {
  253. Successes []cdssdk.ObjectID `json:"successes"`
  254. }
  255. func (r *ObjectMoveResp) ParseResponse(resp *http.Response) error {
  256. return sdks.ParseCodeDataJSONResponse(resp, r)
  257. }
  258. func (c *ObjectService) Move(req ObjectMove) (*ObjectMoveResp, error) {
  259. return JSONAPI[*ObjectMoveResp](c.cfg, http.DefaultClient, &req)
  260. }
  261. const ObjectDeletePath = "/object/delete"
  262. type ObjectDelete struct {
  263. UserID cdssdk.UserID `json:"userID" binding:"required"`
  264. ObjectIDs []cdssdk.ObjectID `json:"objectIDs" binding:"required"`
  265. }
  266. func (r *ObjectDelete) MakeParam() *sdks.RequestParam {
  267. return sdks.MakeJSONParam(http.MethodPost, ObjectDeletePath, r)
  268. }
  269. type ObjectDeleteResp struct{}
  270. func (r *ObjectDeleteResp) ParseResponse(resp *http.Response) error {
  271. return sdks.ParseCodeDataJSONResponse(resp, r)
  272. }
  273. func (c *ObjectService) Delete(req ObjectDelete) error {
  274. return JSONAPINoData[*ObjectDeleteResp](c.cfg, http.DefaultClient, &req)
  275. }
  276. const ObjectDeleteByPathPath = "/object/deleteByPath"
  277. type ObjectDeleteByPath struct {
  278. UserID cdssdk.UserID `json:"userID" binding:"required"`
  279. PackageID cdssdk.PackageID `json:"packageID" binding:"required"`
  280. Path string `json:"path" binding:"required"`
  281. }
  282. func (r *ObjectDeleteByPath) MakeParam() *sdks.RequestParam {
  283. return sdks.MakeJSONParam(http.MethodPost, ObjectDeleteByPathPath, r)
  284. }
  285. type ObjectDeleteByPathResp struct{}
  286. func (r *ObjectDeleteByPathResp) ParseResponse(resp *http.Response) error {
  287. return sdks.ParseCodeDataJSONResponse(resp, r)
  288. }
  289. func (c *ObjectService) DeleteByPath(req ObjectDeleteByPath) error {
  290. return JSONAPINoData[*ObjectDeleteByPathResp](c.cfg, http.DefaultClient, &req)
  291. }
  292. const ObjectClonePath = "/object/clone"
  293. type ObjectClone struct {
  294. UserID cdssdk.UserID `json:"userID" binding:"required"`
  295. Clonings []CloningObject `json:"clonings" binding:"required"`
  296. }
  297. func (r *ObjectClone) MakeParam() *sdks.RequestParam {
  298. return sdks.MakeJSONParam(http.MethodPost, ObjectClonePath, r)
  299. }
  300. type CloningObject struct {
  301. ObjectID cdssdk.ObjectID `json:"objectID" binding:"required"`
  302. NewPath string `json:"newPath" binding:"required"`
  303. NewPackageID cdssdk.PackageID `json:"newPackageID" binding:"required"`
  304. }
  305. type ObjectCloneResp struct {
  306. Objects []*cdssdk.Object `json:"objects"`
  307. }
  308. func (r *ObjectCloneResp) ParseResponse(resp *http.Response) error {
  309. return sdks.ParseCodeDataJSONResponse(resp, r)
  310. }
  311. func (c *ObjectService) Clone(req ObjectClone) (*ObjectCloneResp, error) {
  312. return JSONAPI[*ObjectCloneResp](c.cfg, http.DefaultClient, &req)
  313. }
  314. const ObjectGetPackageObjectsPath = "/object/getPackageObjects"
  315. type ObjectGetPackageObjects struct {
  316. UserID cdssdk.UserID `form:"userID" url:"userID" binding:"required"`
  317. PackageID cdssdk.PackageID `form:"packageID" url:"packageID" binding:"required"`
  318. }
  319. func (r *ObjectGetPackageObjects) MakeParam() *sdks.RequestParam {
  320. return sdks.MakeQueryParam(http.MethodGet, ObjectGetPackageObjectsPath, r)
  321. }
  322. type ObjectGetPackageObjectsResp struct {
  323. Objects []cdssdk.Object `json:"objects"`
  324. }
  325. func (r *ObjectGetPackageObjectsResp) ParseResponse(resp *http.Response) error {
  326. return sdks.ParseCodeDataJSONResponse(resp, r)
  327. }
  328. func (c *ObjectService) GetPackageObjects(req ObjectGetPackageObjects) (*ObjectGetPackageObjectsResp, error) {
  329. return JSONAPI[*ObjectGetPackageObjectsResp](c.cfg, http.DefaultClient, &req)
  330. }
  331. const ObjectNewMultipartUploadPath = "/v1/object/newMultipartUpload"
  332. type ObjectNewMultipartUpload struct {
  333. UserID cdssdk.UserID `json:"userID" binding:"required"`
  334. PackageID cdssdk.PackageID `json:"packageID" binding:"required"`
  335. Path string `json:"path" binding:"required"`
  336. }
  337. func (r *ObjectNewMultipartUpload) MakeParam() *sdks.RequestParam {
  338. return sdks.MakeJSONParam(http.MethodPost, ObjectNewMultipartUploadPath, r)
  339. }
  340. type ObjectNewMultipartUploadResp struct {
  341. Object cdssdk.Object `json:"object"`
  342. }
  343. func (r *ObjectNewMultipartUploadResp) ParseResponse(resp *http.Response) error {
  344. return sdks.ParseCodeDataJSONResponse(resp, r)
  345. }
  346. func (c *ObjectService) NewMultipartUpload(req ObjectNewMultipartUpload) (*ObjectNewMultipartUploadResp, error) {
  347. return JSONAPI[*ObjectNewMultipartUploadResp](c.cfg, http.DefaultClient, &req)
  348. }
  349. const ObjectUploadPartPath = "/v1/object/uploadPart"
  350. type ObjectUploadPart struct {
  351. ObjectUploadPartInfo
  352. File io.ReadCloser `json:"-"`
  353. }
  354. type ObjectUploadPartInfo struct {
  355. UserID cdssdk.UserID `json:"userID" binding:"required"`
  356. ObjectID cdssdk.ObjectID `json:"objectID" binding:"required"`
  357. Index int `json:"index"`
  358. }
  359. type ObjectUploadPartResp struct{}
  360. func (c *ObjectService) UploadPart(req ObjectUploadPart) (*ObjectUploadPartResp, error) {
  361. url, err := url.JoinPath(c.cfg.URL, ObjectUploadPartPath)
  362. if err != nil {
  363. return nil, err
  364. }
  365. infoJSON, err := serder.ObjectToJSON(req)
  366. if err != nil {
  367. return nil, fmt.Errorf("upload info to json: %w", err)
  368. }
  369. resp, err := http2.PostMultiPart(url, http2.MultiPartRequestParam{
  370. Form: map[string]string{"info": string(infoJSON)},
  371. Files: iterator.Array(&http2.IterMultiPartFile{
  372. FieldName: "file",
  373. File: req.File,
  374. }),
  375. })
  376. if err != nil {
  377. return nil, err
  378. }
  379. contType := resp.Header.Get("Content-Type")
  380. if strings.Contains(contType, http2.ContentTypeJSON) {
  381. var err error
  382. var codeResp response[ObjectUploadPartResp]
  383. if codeResp, err = serder.JSONToObjectStreamEx[response[ObjectUploadPartResp]](resp.Body); err != nil {
  384. return nil, fmt.Errorf("parsing response: %w", err)
  385. }
  386. if codeResp.Code == errorcode.OK {
  387. return &codeResp.Data, nil
  388. }
  389. return nil, codeResp.ToError()
  390. }
  391. return nil, fmt.Errorf("unknow response content type: %s", contType)
  392. }
  393. const ObjectCompleteMultipartUploadPath = "/v1/object/completeMultipartUpload"
  394. type ObjectCompleteMultipartUpload struct {
  395. UserID cdssdk.UserID `json:"userID" binding:"required"`
  396. ObjectID cdssdk.ObjectID `json:"objectID" binding:"required"`
  397. Indexes []int `json:"indexes" binding:"required"`
  398. }
  399. func (r *ObjectCompleteMultipartUpload) MakeParam() *sdks.RequestParam {
  400. return sdks.MakeJSONParam(http.MethodPost, ObjectCompleteMultipartUploadPath, r)
  401. }
  402. type ObjectCompleteMultipartUploadResp struct {
  403. Object cdssdk.Object `json:"object"`
  404. }
  405. func (r *ObjectCompleteMultipartUploadResp) ParseResponse(resp *http.Response) error {
  406. return sdks.ParseCodeDataJSONResponse(resp, r)
  407. }
  408. func (c *ObjectService) CompleteMultipartUpload(req ObjectCompleteMultipartUpload) (*ObjectCompleteMultipartUploadResp, error) {
  409. return JSONAPI[*ObjectCompleteMultipartUploadResp](c.cfg, http.DefaultClient, &req)
  410. }