multipart.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. package oss
  2. import (
  3. "bytes"
  4. "encoding/xml"
  5. "io"
  6. "net/http"
  7. "net/url"
  8. "os"
  9. "sort"
  10. "strconv"
  11. )
  12. // InitiateMultipartUpload initializes multipart upload
  13. //
  14. // objectKey object name
  15. // options the object constricts for upload. The valid options are CacheControl, ContentDisposition, ContentEncoding, Expires,
  16. // ServerSideEncryption, Meta, check out the following link:
  17. // https://help.aliyun.com/document_detail/oss/api-reference/multipart-upload/InitiateMultipartUpload.html
  18. //
  19. // InitiateMultipartUploadResult the return value of the InitiateMultipartUpload, which is used for calls later on such as UploadPartFromFile,UploadPartCopy.
  20. // error it's nil if the operation succeeds, otherwise it's an error object.
  21. //
  22. func (bucket Bucket) InitiateMultipartUpload(objectKey string, options ...Option) (InitiateMultipartUploadResult, error) {
  23. var imur InitiateMultipartUploadResult
  24. opts := AddContentType(options, objectKey)
  25. params := map[string]interface{}{}
  26. params["uploads"] = nil
  27. resp, err := bucket.do("POST", objectKey, params, opts, nil, nil)
  28. if err != nil {
  29. return imur, err
  30. }
  31. defer resp.Body.Close()
  32. err = xmlUnmarshal(resp.Body, &imur)
  33. return imur, err
  34. }
  35. // UploadPart uploads parts
  36. //
  37. // After initializing a Multipart Upload, the upload Id and object key could be used for uploading the parts.
  38. // Each part has its part number (ranges from 1 to 10,000). And for each upload Id, the part number identifies the position of the part in the whole file.
  39. // And thus with the same part number and upload Id, another part upload will overwrite the data.
  40. // Except the last one, minimal part size is 100KB. There's no limit on the last part size.
  41. //
  42. // imur the returned value of InitiateMultipartUpload.
  43. // reader io.Reader the reader for the part's data.
  44. // size the part size.
  45. // partNumber the part number (ranges from 1 to 10,000). Invalid part number will lead to InvalidArgument error.
  46. //
  47. // UploadPart the return value of the upload part. It consists of PartNumber and ETag. It's valid when error is nil.
  48. // error it's nil if the operation succeeds, otherwise it's an error object.
  49. //
  50. func (bucket Bucket) UploadPart(imur InitiateMultipartUploadResult, reader io.Reader,
  51. partSize int64, partNumber int, options ...Option) (UploadPart, error) {
  52. request := &UploadPartRequest{
  53. InitResult: &imur,
  54. Reader: reader,
  55. PartSize: partSize,
  56. PartNumber: partNumber,
  57. }
  58. result, err := bucket.DoUploadPart(request, options)
  59. return result.Part, err
  60. }
  61. // UploadPartFromFile uploads part from the file.
  62. //
  63. // imur the return value of a successful InitiateMultipartUpload.
  64. // filePath the local file path to upload.
  65. // startPosition the start position in the local file.
  66. // partSize the part size.
  67. // partNumber the part number (from 1 to 10,000)
  68. //
  69. // UploadPart the return value consists of PartNumber and ETag.
  70. // error it's nil if the operation succeeds, otherwise it's an error object.
  71. //
  72. func (bucket Bucket) UploadPartFromFile(imur InitiateMultipartUploadResult, filePath string,
  73. startPosition, partSize int64, partNumber int, options ...Option) (UploadPart, error) {
  74. var part = UploadPart{}
  75. fd, err := os.Open(filePath)
  76. if err != nil {
  77. return part, err
  78. }
  79. defer fd.Close()
  80. fd.Seek(startPosition, os.SEEK_SET)
  81. request := &UploadPartRequest{
  82. InitResult: &imur,
  83. Reader: fd,
  84. PartSize: partSize,
  85. PartNumber: partNumber,
  86. }
  87. result, err := bucket.DoUploadPart(request, options)
  88. return result.Part, err
  89. }
  90. // DoUploadPart does the actual part upload.
  91. //
  92. // request part upload request
  93. //
  94. // UploadPartResult the result of uploading part.
  95. // error it's nil if the operation succeeds, otherwise it's an error object.
  96. //
  97. func (bucket Bucket) DoUploadPart(request *UploadPartRequest, options []Option) (*UploadPartResult, error) {
  98. listener := GetProgressListener(options)
  99. options = append(options, ContentLength(request.PartSize))
  100. params := map[string]interface{}{}
  101. params["partNumber"] = strconv.Itoa(request.PartNumber)
  102. params["uploadId"] = request.InitResult.UploadID
  103. resp, err := bucket.do("PUT", request.InitResult.Key, params, options,
  104. &io.LimitedReader{R: request.Reader, N: request.PartSize}, listener)
  105. if err != nil {
  106. return &UploadPartResult{}, err
  107. }
  108. defer resp.Body.Close()
  109. part := UploadPart{
  110. ETag: resp.Headers.Get(HTTPHeaderEtag),
  111. PartNumber: request.PartNumber,
  112. }
  113. if bucket.GetConfig().IsEnableCRC {
  114. err = CheckCRC(resp, "DoUploadPart")
  115. if err != nil {
  116. return &UploadPartResult{part}, err
  117. }
  118. }
  119. return &UploadPartResult{part}, nil
  120. }
  121. // UploadPartCopy uploads part copy
  122. //
  123. // imur the return value of InitiateMultipartUpload
  124. // copySrc source Object name
  125. // startPosition the part's start index in the source file
  126. // partSize the part size
  127. // partNumber the part number, ranges from 1 to 10,000. If it exceeds the range OSS returns InvalidArgument error.
  128. // options the constraints of source object for the copy. The copy happens only when these contraints are met. Otherwise it returns error.
  129. // CopySourceIfNoneMatch, CopySourceIfModifiedSince CopySourceIfUnmodifiedSince, check out the following link for the detail
  130. // https://help.aliyun.com/document_detail/oss/api-reference/multipart-upload/UploadPartCopy.html
  131. //
  132. // UploadPart the return value consists of PartNumber and ETag.
  133. // error it's nil if the operation succeeds, otherwise it's an error object.
  134. //
  135. func (bucket Bucket) UploadPartCopy(imur InitiateMultipartUploadResult, srcBucketName, srcObjectKey string,
  136. startPosition, partSize int64, partNumber int, options ...Option) (UploadPart, error) {
  137. var out UploadPartCopyResult
  138. var part UploadPart
  139. var opts []Option
  140. //first find version id
  141. versionIdKey := "versionId"
  142. versionId, _ := FindOption(options, versionIdKey, nil)
  143. if versionId == nil {
  144. opts = []Option{CopySource(srcBucketName, url.QueryEscape(srcObjectKey)),
  145. CopySourceRange(startPosition, partSize)}
  146. } else {
  147. opts = []Option{CopySourceVersion(srcBucketName, url.QueryEscape(srcObjectKey), versionId.(string)),
  148. CopySourceRange(startPosition, partSize)}
  149. options = DeleteOption(options, versionIdKey)
  150. }
  151. opts = append(opts, options...)
  152. params := map[string]interface{}{}
  153. params["partNumber"] = strconv.Itoa(partNumber)
  154. params["uploadId"] = imur.UploadID
  155. resp, err := bucket.do("PUT", imur.Key, params, opts, nil, nil)
  156. if err != nil {
  157. return part, err
  158. }
  159. defer resp.Body.Close()
  160. err = xmlUnmarshal(resp.Body, &out)
  161. if err != nil {
  162. return part, err
  163. }
  164. part.ETag = out.ETag
  165. part.PartNumber = partNumber
  166. return part, nil
  167. }
  168. // CompleteMultipartUpload completes the multipart upload.
  169. //
  170. // imur the return value of InitiateMultipartUpload.
  171. // parts the array of return value of UploadPart/UploadPartFromFile/UploadPartCopy.
  172. //
  173. // CompleteMultipartUploadResponse the return value when the call succeeds. Only valid when the error is nil.
  174. // error it's nil if the operation succeeds, otherwise it's an error object.
  175. //
  176. func (bucket Bucket) CompleteMultipartUpload(imur InitiateMultipartUploadResult,
  177. parts []UploadPart, options ...Option) (CompleteMultipartUploadResult, error) {
  178. var out CompleteMultipartUploadResult
  179. sort.Sort(UploadParts(parts))
  180. cxml := completeMultipartUploadXML{}
  181. cxml.Part = parts
  182. bs, err := xml.Marshal(cxml)
  183. if err != nil {
  184. return out, err
  185. }
  186. buffer := new(bytes.Buffer)
  187. buffer.Write(bs)
  188. params := map[string]interface{}{}
  189. params["uploadId"] = imur.UploadID
  190. resp, err := bucket.do("POST", imur.Key, params, options, buffer, nil)
  191. if err != nil {
  192. return out, err
  193. }
  194. defer resp.Body.Close()
  195. err = xmlUnmarshal(resp.Body, &out)
  196. return out, err
  197. }
  198. // AbortMultipartUpload aborts the multipart upload.
  199. //
  200. // imur the return value of InitiateMultipartUpload.
  201. //
  202. // error it's nil if the operation succeeds, otherwise it's an error object.
  203. //
  204. func (bucket Bucket) AbortMultipartUpload(imur InitiateMultipartUploadResult, options ...Option) error {
  205. params := map[string]interface{}{}
  206. params["uploadId"] = imur.UploadID
  207. resp, err := bucket.do("DELETE", imur.Key, params, options, nil, nil)
  208. if err != nil {
  209. return err
  210. }
  211. defer resp.Body.Close()
  212. return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
  213. }
  214. // ListUploadedParts lists the uploaded parts.
  215. //
  216. // imur the return value of InitiateMultipartUpload.
  217. //
  218. // ListUploadedPartsResponse the return value if it succeeds, only valid when error is nil.
  219. // error it's nil if the operation succeeds, otherwise it's an error object.
  220. //
  221. func (bucket Bucket) ListUploadedParts(imur InitiateMultipartUploadResult, options ...Option) (ListUploadedPartsResult, error) {
  222. var out ListUploadedPartsResult
  223. options = append(options, EncodingType("url"))
  224. params := map[string]interface{}{}
  225. params, err := GetRawParams(options)
  226. if err != nil {
  227. return out, err
  228. }
  229. params["uploadId"] = imur.UploadID
  230. resp, err := bucket.do("GET", imur.Key, params, options, nil, nil)
  231. if err != nil {
  232. return out, err
  233. }
  234. defer resp.Body.Close()
  235. err = xmlUnmarshal(resp.Body, &out)
  236. if err != nil {
  237. return out, err
  238. }
  239. err = decodeListUploadedPartsResult(&out)
  240. return out, err
  241. }
  242. // ListMultipartUploads lists all ongoing multipart upload tasks
  243. //
  244. // options listObject's filter. Prefix specifies the returned object's prefix; KeyMarker specifies the returned object's start point in lexicographic order;
  245. // MaxKeys specifies the max entries to return; Delimiter is the character for grouping object keys.
  246. //
  247. // ListMultipartUploadResponse the return value if it succeeds, only valid when error is nil.
  248. // error it's nil if the operation succeeds, otherwise it's an error object.
  249. //
  250. func (bucket Bucket) ListMultipartUploads(options ...Option) (ListMultipartUploadResult, error) {
  251. var out ListMultipartUploadResult
  252. options = append(options, EncodingType("url"))
  253. params, err := GetRawParams(options)
  254. if err != nil {
  255. return out, err
  256. }
  257. params["uploads"] = nil
  258. resp, err := bucket.do("GET", "", params, options, nil, nil)
  259. if err != nil {
  260. return out, err
  261. }
  262. defer resp.Body.Close()
  263. err = xmlUnmarshal(resp.Body, &out)
  264. if err != nil {
  265. return out, err
  266. }
  267. err = decodeListMultipartUploadResult(&out)
  268. return out, err
  269. }