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