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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  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. 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)
  93. if err != nil {
  94. return nil, fmt.Errorf("upload info to json: %w", err)
  95. }
  96. resp, err := PostMultiPart(&c.cfg, 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. httpReq, err := req.MakeParam().MakeRequest(c.cfg.EndPoint)
  137. if err != nil {
  138. return nil, err
  139. }
  140. resp, err := c.httpCli.Do(httpReq)
  141. if err != nil {
  142. return nil, err
  143. }
  144. contType := resp.Header.Get("Content-Type")
  145. if strings.Contains(contType, http2.ContentTypeJSON) {
  146. var codeResp response[any]
  147. if err := serder.JSONToObjectStream(resp.Body, &codeResp); err != nil {
  148. return nil, fmt.Errorf("parsing response: %w", err)
  149. }
  150. return nil, codeResp.ToError()
  151. }
  152. _, params, err := mime.ParseMediaType(resp.Header.Get("Content-Disposition"))
  153. if err != nil {
  154. return nil, fmt.Errorf("parsing content disposition: %w", err)
  155. }
  156. return &DownloadingObject{
  157. Path: params["filename"],
  158. File: resp.Body,
  159. }, nil
  160. }
  161. const ObjectDownloadByPathPath = "/object/downloadByPath"
  162. type ObjectDownloadByPath struct {
  163. PackageID types.PackageID `form:"packageID" url:"packageID" binding:"required"`
  164. Path string `form:"path" url:"path" binding:"required"`
  165. Offset int64 `form:"offset" url:"offset,omitempty"`
  166. Length *int64 `form:"length" url:"length,omitempty"`
  167. }
  168. func (r *ObjectDownloadByPath) MakeParam() *sdks.RequestParam {
  169. return sdks.MakeQueryParam(http.MethodGet, ObjectDownloadByPathPath, r)
  170. }
  171. func (c *ObjectService) DownloadByPath(req ObjectDownloadByPath) (*DownloadingObject, error) {
  172. httpReq, err := req.MakeParam().MakeRequest(c.cfg.EndPoint)
  173. if err != nil {
  174. return nil, err
  175. }
  176. resp, err := c.httpCli.Do(httpReq)
  177. if err != nil {
  178. return nil, err
  179. }
  180. contType := resp.Header.Get("Content-Type")
  181. if strings.Contains(contType, http2.ContentTypeJSON) {
  182. var codeResp response[any]
  183. if err := serder.JSONToObjectStream(resp.Body, &codeResp); err != nil {
  184. return nil, fmt.Errorf("parsing response: %w", err)
  185. }
  186. return nil, codeResp.ToError()
  187. }
  188. _, params, err := mime.ParseMediaType(resp.Header.Get("Content-Disposition"))
  189. if err != nil {
  190. return nil, fmt.Errorf("parsing content disposition: %w", err)
  191. }
  192. return &DownloadingObject{
  193. Path: params["filename"],
  194. File: resp.Body,
  195. }, nil
  196. }
  197. const ObjectUpdateInfoPath = "/object/updateInfo"
  198. type UpdatingObject struct {
  199. ObjectID types.ObjectID `json:"objectID" binding:"required"`
  200. UpdateTime time.Time `json:"updateTime" binding:"required"`
  201. }
  202. func (u *UpdatingObject) ApplyTo(obj *types.Object) {
  203. obj.UpdateTime = u.UpdateTime
  204. }
  205. type ObjectUpdateInfo struct {
  206. Updatings []UpdatingObject `json:"updatings" binding:"required"`
  207. }
  208. func (r *ObjectUpdateInfo) MakeParam() *sdks.RequestParam {
  209. return sdks.MakeJSONParam(http.MethodPost, ObjectUpdateInfoPath, r)
  210. }
  211. type ObjectUpdateInfoResp struct {
  212. Successes []types.ObjectID `json:"successes"`
  213. }
  214. func (r *ObjectUpdateInfoResp) ParseResponse(resp *http.Response) error {
  215. return sdks.ParseCodeDataJSONResponse(resp, r)
  216. }
  217. func (c *ObjectService) UpdateInfo(req ObjectUpdateInfo) (*ObjectUpdateInfoResp, error) {
  218. return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectUpdateInfoResp{})
  219. }
  220. const ObjectUpdateInfoByPathPath = "/object/updateInfoByPath"
  221. type ObjectUpdateInfoByPath struct {
  222. PackageID types.PackageID `json:"packageID" binding:"required"`
  223. Path string `json:"path" binding:"required"`
  224. UpdateTime time.Time `json:"updateTime" binding:"required"`
  225. }
  226. func (r *ObjectUpdateInfoByPath) MakeParam() *sdks.RequestParam {
  227. return sdks.MakeJSONParam(http.MethodPost, ObjectUpdateInfoByPathPath, r)
  228. }
  229. type ObjectUpdateInfoByPathResp struct{}
  230. func (r *ObjectUpdateInfoByPathResp) ParseResponse(resp *http.Response) error {
  231. return sdks.ParseCodeDataJSONResponse(resp, r)
  232. }
  233. func (c *ObjectService) UpdateInfoByPath(req ObjectUpdateInfoByPath) (*ObjectUpdateInfoByPathResp, error) {
  234. return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectUpdateInfoByPathResp{})
  235. }
  236. const ObjectMovePath = "/object/move"
  237. type MovingObject struct {
  238. ObjectID types.ObjectID `json:"objectID" binding:"required"`
  239. PackageID types.PackageID `json:"packageID" binding:"required"`
  240. Path string `json:"path" binding:"required"`
  241. }
  242. func (m *MovingObject) ApplyTo(obj *types.Object) {
  243. obj.PackageID = m.PackageID
  244. obj.Path = m.Path
  245. }
  246. type ObjectMove struct {
  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 []types.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(&c.cfg, c.httpCli, &req, &ObjectMoveResp{})
  260. }
  261. const ObjectDeletePath = "/object/delete"
  262. type ObjectDelete struct {
  263. ObjectIDs []types.ObjectID `json:"objectIDs" binding:"required"`
  264. }
  265. func (r *ObjectDelete) MakeParam() *sdks.RequestParam {
  266. return sdks.MakeJSONParam(http.MethodPost, ObjectDeletePath, r)
  267. }
  268. type ObjectDeleteResp struct{}
  269. func (r *ObjectDeleteResp) ParseResponse(resp *http.Response) error {
  270. return sdks.ParseCodeDataJSONResponse(resp, r)
  271. }
  272. func (c *ObjectService) Delete(req ObjectDelete) error {
  273. return JSONAPINoData(&c.cfg, c.httpCli, &req)
  274. }
  275. const ObjectDeleteByPathPath = "/object/deleteByPath"
  276. type ObjectDeleteByPath struct {
  277. PackageID types.PackageID `json:"packageID" binding:"required"`
  278. Path string `json:"path" binding:"required"`
  279. }
  280. func (r *ObjectDeleteByPath) MakeParam() *sdks.RequestParam {
  281. return sdks.MakeJSONParam(http.MethodPost, ObjectDeleteByPathPath, r)
  282. }
  283. type ObjectDeleteByPathResp struct{}
  284. func (r *ObjectDeleteByPathResp) ParseResponse(resp *http.Response) error {
  285. return sdks.ParseCodeDataJSONResponse(resp, r)
  286. }
  287. func (c *ObjectService) DeleteByPath(req ObjectDeleteByPath) error {
  288. return JSONAPINoData(&c.cfg, c.httpCli, &req)
  289. }
  290. const ObjectClonePath = "/object/clone"
  291. type ObjectClone struct {
  292. Clonings []CloningObject `json:"clonings" binding:"required"`
  293. }
  294. func (r *ObjectClone) MakeParam() *sdks.RequestParam {
  295. return sdks.MakeJSONParam(http.MethodPost, ObjectClonePath, r)
  296. }
  297. type CloningObject struct {
  298. ObjectID types.ObjectID `json:"objectID" binding:"required"`
  299. NewPath string `json:"newPath" binding:"required"`
  300. NewPackageID types.PackageID `json:"newPackageID" binding:"required"`
  301. }
  302. type ObjectCloneResp struct {
  303. Objects []*types.Object `json:"objects"`
  304. }
  305. func (r *ObjectCloneResp) ParseResponse(resp *http.Response) error {
  306. return sdks.ParseCodeDataJSONResponse(resp, r)
  307. }
  308. func (c *ObjectService) Clone(req ObjectClone) (*ObjectCloneResp, error) {
  309. return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectCloneResp{})
  310. }
  311. const ObjectGetPackageObjectsPath = "/object/getPackageObjects"
  312. type ObjectGetPackageObjects struct {
  313. PackageID types.PackageID `form:"packageID" url:"packageID" binding:"required"`
  314. }
  315. func (r *ObjectGetPackageObjects) MakeParam() *sdks.RequestParam {
  316. return sdks.MakeQueryParam(http.MethodGet, ObjectGetPackageObjectsPath, r)
  317. }
  318. type ObjectGetPackageObjectsResp struct {
  319. Objects []types.Object `json:"objects"`
  320. }
  321. func (r *ObjectGetPackageObjectsResp) ParseResponse(resp *http.Response) error {
  322. return sdks.ParseCodeDataJSONResponse(resp, r)
  323. }
  324. func (c *ObjectService) GetPackageObjects(req ObjectGetPackageObjects) (*ObjectGetPackageObjectsResp, error) {
  325. return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectGetPackageObjectsResp{})
  326. }
  327. const ObjectNewMultipartUploadPath = "/object/newMultipartUpload"
  328. type ObjectNewMultipartUpload struct {
  329. PackageID types.PackageID `json:"packageID" binding:"required"`
  330. Path string `json:"path" binding:"required"`
  331. }
  332. func (r *ObjectNewMultipartUpload) MakeParam() *sdks.RequestParam {
  333. return sdks.MakeJSONParam(http.MethodPost, ObjectNewMultipartUploadPath, r)
  334. }
  335. type ObjectNewMultipartUploadResp struct {
  336. Object types.Object `json:"object"`
  337. }
  338. func (r *ObjectNewMultipartUploadResp) ParseResponse(resp *http.Response) error {
  339. return sdks.ParseCodeDataJSONResponse(resp, r)
  340. }
  341. func (c *ObjectService) NewMultipartUpload(req ObjectNewMultipartUpload) (*ObjectNewMultipartUploadResp, error) {
  342. return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectNewMultipartUploadResp{})
  343. }
  344. const ObjectUploadPartPath = "/object/uploadPart"
  345. type ObjectUploadPart struct {
  346. ObjectUploadPartInfo
  347. File io.ReadCloser `json:"-"`
  348. }
  349. type ObjectUploadPartInfo struct {
  350. ObjectID types.ObjectID `json:"objectID" binding:"required"`
  351. Index int `json:"index"`
  352. }
  353. type ObjectUploadPartResp struct{}
  354. func (c *ObjectService) UploadPart(req ObjectUploadPart) (*ObjectUploadPartResp, error) {
  355. url, err := url.JoinPath(c.cfg.EndPoint, ObjectUploadPartPath)
  356. if err != nil {
  357. return nil, err
  358. }
  359. infoJSON, err := serder.ObjectToJSON(req)
  360. if err != nil {
  361. return nil, fmt.Errorf("upload info to json: %w", err)
  362. }
  363. resp, err := http2.PostMultiPart(url, http2.MultiPartRequestParam{
  364. Form: map[string]string{"info": string(infoJSON)},
  365. Files: iterator.Array(&http2.IterMultiPartFile{
  366. FieldName: "file",
  367. File: req.File,
  368. }),
  369. })
  370. if err != nil {
  371. return nil, err
  372. }
  373. contType := resp.Header.Get("Content-Type")
  374. if strings.Contains(contType, http2.ContentTypeJSON) {
  375. var err error
  376. var codeResp response[ObjectUploadPartResp]
  377. if codeResp, err = serder.JSONToObjectStreamEx[response[ObjectUploadPartResp]](resp.Body); err != nil {
  378. return nil, fmt.Errorf("parsing response: %w", err)
  379. }
  380. if codeResp.Code == errorcode.OK {
  381. return &codeResp.Data, nil
  382. }
  383. return nil, codeResp.ToError()
  384. }
  385. return nil, fmt.Errorf("unknow response content type: %s", contType)
  386. }
  387. const ObjectCompleteMultipartUploadPath = "/object/completeMultipartUpload"
  388. type ObjectCompleteMultipartUpload struct {
  389. ObjectID types.ObjectID `json:"objectID" binding:"required"`
  390. Indexes []int `json:"indexes" binding:"required"`
  391. }
  392. func (r *ObjectCompleteMultipartUpload) MakeParam() *sdks.RequestParam {
  393. return sdks.MakeJSONParam(http.MethodPost, ObjectCompleteMultipartUploadPath, r)
  394. }
  395. type ObjectCompleteMultipartUploadResp struct {
  396. Object types.Object `json:"object"`
  397. }
  398. func (r *ObjectCompleteMultipartUploadResp) ParseResponse(resp *http.Response) error {
  399. return sdks.ParseCodeDataJSONResponse(resp, r)
  400. }
  401. func (c *ObjectService) CompleteMultipartUpload(req ObjectCompleteMultipartUpload) (*ObjectCompleteMultipartUploadResp, error) {
  402. return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectCompleteMultipartUploadResp{})
  403. }

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