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

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

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