OSDN Git Service

Create ossClient.go (#574)
[bytom/vapor.git] / vendor / github.com / aliyun / aliyun-oss-go-sdk / oss / bucket.go
1 package oss
2
3 import (
4         "bytes"
5         "crypto/md5"
6         "encoding/base64"
7         "encoding/xml"
8         "fmt"
9         "hash"
10         "hash/crc64"
11         "io"
12         "net/http"
13         "net/url"
14         "os"
15         "strconv"
16         "strings"
17         "time"
18 )
19
20 // Bucket implements the operations of object.
21 type Bucket struct {
22         Client     Client
23         BucketName string
24 }
25
26 // PutObject creates a new object and it will overwrite the original one if it exists already.
27 //
28 // objectKey    the object key in UTF-8 encoding. The length must be between 1 and 1023, and cannot start with "/" or "\".
29 // reader    io.Reader instance for reading the data for uploading
30 // options    the options for uploading the object. The valid options here are CacheControl, ContentDisposition, ContentEncoding
31 //            Expires, ServerSideEncryption, ObjectACL and Meta. Refer to the link below for more details.
32 //            https://help.aliyun.com/document_detail/oss/api-reference/object/PutObject.html
33 //
34 // error    it's nil if no error, otherwise it's an error object.
35 //
36 func (bucket Bucket) PutObject(objectKey string, reader io.Reader, options ...Option) error {
37         opts := AddContentType(options, objectKey)
38
39         request := &PutObjectRequest{
40                 ObjectKey: objectKey,
41                 Reader:    reader,
42         }
43         resp, err := bucket.DoPutObject(request, opts)
44         if err != nil {
45                 return err
46         }
47         defer resp.Body.Close()
48
49         return err
50 }
51
52 // PutObjectFromFile creates a new object from the local file.
53 //
54 // objectKey    object key.
55 // filePath    the local file path to upload.
56 // options    the options for uploading the object. Refer to the parameter options in PutObject for more details.
57 //
58 // error    it's nil if no error, otherwise it's an error object.
59 //
60 func (bucket Bucket) PutObjectFromFile(objectKey, filePath string, options ...Option) error {
61         fd, err := os.Open(filePath)
62         if err != nil {
63                 return err
64         }
65         defer fd.Close()
66
67         opts := AddContentType(options, filePath, objectKey)
68
69         request := &PutObjectRequest{
70                 ObjectKey: objectKey,
71                 Reader:    fd,
72         }
73         resp, err := bucket.DoPutObject(request, opts)
74         if err != nil {
75                 return err
76         }
77         defer resp.Body.Close()
78
79         return err
80 }
81
82 // DoPutObject does the actual upload work.
83 //
84 // request    the request instance for uploading an object.
85 // options    the options for uploading an object.
86 //
87 // Response    the response from OSS.
88 // error    it's nil if no error, otherwise it's an error object.
89 //
90 func (bucket Bucket) DoPutObject(request *PutObjectRequest, options []Option) (*Response, error) {
91         isOptSet, _, _ := IsOptionSet(options, HTTPHeaderContentType)
92         if !isOptSet {
93                 options = AddContentType(options, request.ObjectKey)
94         }
95
96         listener := GetProgressListener(options)
97
98         params := map[string]interface{}{}
99         resp, err := bucket.do("PUT", request.ObjectKey, params, options, request.Reader, listener)
100         if err != nil {
101                 return nil, err
102         }
103
104         if bucket.GetConfig().IsEnableCRC {
105                 err = CheckCRC(resp, "DoPutObject")
106                 if err != nil {
107                         return resp, err
108                 }
109         }
110
111         err = CheckRespCode(resp.StatusCode, []int{http.StatusOK})
112
113         return resp, err
114 }
115
116 // GetObject downloads the object.
117 //
118 // objectKey    the object key.
119 // options    the options for downloading the object. The valid values are: Range, IfModifiedSince, IfUnmodifiedSince, IfMatch,
120 //            IfNoneMatch, AcceptEncoding. For more details, please check out:
121 //            https://help.aliyun.com/document_detail/oss/api-reference/object/GetObject.html
122 //
123 // io.ReadCloser    reader instance for reading data from response. It must be called close() after the usage and only valid when error is nil.
124 // error    it's nil if no error, otherwise it's an error object.
125 //
126 func (bucket Bucket) GetObject(objectKey string, options ...Option) (io.ReadCloser, error) {
127         result, err := bucket.DoGetObject(&GetObjectRequest{objectKey}, options)
128         if err != nil {
129                 return nil, err
130         }
131
132         return result.Response, nil
133 }
134
135 // GetObjectToFile downloads the data to a local file.
136 //
137 // objectKey    the object key to download.
138 // filePath    the local file to store the object data.
139 // options    the options for downloading the object. Refer to the parameter options in method GetObject for more details.
140 //
141 // error    it's nil if no error, otherwise it's an error object.
142 //
143 func (bucket Bucket) GetObjectToFile(objectKey, filePath string, options ...Option) error {
144         tempFilePath := filePath + TempFileSuffix
145
146         // Calls the API to actually download the object. Returns the result instance.
147         result, err := bucket.DoGetObject(&GetObjectRequest{objectKey}, options)
148         if err != nil {
149                 return err
150         }
151         defer result.Response.Close()
152
153         // If the local file does not exist, create a new one. If it exists, overwrite it.
154         fd, err := os.OpenFile(tempFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, FilePermMode)
155         if err != nil {
156                 return err
157         }
158
159         // Copy the data to the local file path.
160         _, err = io.Copy(fd, result.Response.Body)
161         fd.Close()
162         if err != nil {
163                 return err
164         }
165
166         // Compares the CRC value
167         hasRange, _, _ := IsOptionSet(options, HTTPHeaderRange)
168         encodeOpt, _ := FindOption(options, HTTPHeaderAcceptEncoding, nil)
169         acceptEncoding := ""
170         if encodeOpt != nil {
171                 acceptEncoding = encodeOpt.(string)
172         }
173         if bucket.GetConfig().IsEnableCRC && !hasRange && acceptEncoding != "gzip" {
174                 result.Response.ClientCRC = result.ClientCRC.Sum64()
175                 err = CheckCRC(result.Response, "GetObjectToFile")
176                 if err != nil {
177                         os.Remove(tempFilePath)
178                         return err
179                 }
180         }
181
182         return os.Rename(tempFilePath, filePath)
183 }
184
185 // DoGetObject is the actual API that gets the object. It's the internal function called by other public APIs.
186 //
187 // request    the request to download the object.
188 // options    the options for downloading the file. Checks out the parameter options in method GetObject.
189 //
190 // GetObjectResult    the result instance of getting the object.
191 // error    it's nil if no error, otherwise it's an error object.
192 //
193 func (bucket Bucket) DoGetObject(request *GetObjectRequest, options []Option) (*GetObjectResult, error) {
194         params, _ := GetRawParams(options)
195         resp, err := bucket.do("GET", request.ObjectKey, params, options, nil, nil)
196         if err != nil {
197                 return nil, err
198         }
199
200         result := &GetObjectResult{
201                 Response: resp,
202         }
203
204         // CRC
205         var crcCalc hash.Hash64
206         hasRange, _, _ := IsOptionSet(options, HTTPHeaderRange)
207         if bucket.GetConfig().IsEnableCRC && !hasRange {
208                 crcCalc = crc64.New(CrcTable())
209                 result.ServerCRC = resp.ServerCRC
210                 result.ClientCRC = crcCalc
211         }
212
213         // Progress
214         listener := GetProgressListener(options)
215
216         contentLen, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderContentLength), 10, 64)
217         resp.Body = TeeReader(resp.Body, crcCalc, contentLen, listener, nil)
218
219         return result, nil
220 }
221
222 // CopyObject copies the object inside the bucket.
223 //
224 // srcObjectKey    the source object to copy.
225 // destObjectKey    the target object to copy.
226 // options    options for copying an object. You can specify the conditions of copy. The valid conditions are CopySourceIfMatch,
227 //            CopySourceIfNoneMatch, CopySourceIfModifiedSince, CopySourceIfUnmodifiedSince, MetadataDirective.
228 //            Also you can specify the target object's attributes, such as CacheControl, ContentDisposition, ContentEncoding, Expires,
229 //            ServerSideEncryption, ObjectACL, Meta. Refer to the link below for more details :
230 //            https://help.aliyun.com/document_detail/oss/api-reference/object/CopyObject.html
231 //
232 // error    it's nil if no error, otherwise it's an error object.
233 //
234 func (bucket Bucket) CopyObject(srcObjectKey, destObjectKey string, options ...Option) (CopyObjectResult, error) {
235         var out CopyObjectResult
236
237         //first find version id
238         versionIdKey := "versionId"
239         versionId, _ := FindOption(options, versionIdKey, nil)
240         if versionId == nil {
241                 options = append(options, CopySource(bucket.BucketName, url.QueryEscape(srcObjectKey)))
242         } else {
243                 options = DeleteOption(options, versionIdKey)
244                 options = append(options, CopySourceVersion(bucket.BucketName, url.QueryEscape(srcObjectKey), versionId.(string)))
245         }
246
247         params := map[string]interface{}{}
248         resp, err := bucket.do("PUT", destObjectKey, params, options, nil, nil)
249         if err != nil {
250                 return out, err
251         }
252         defer resp.Body.Close()
253
254         err = xmlUnmarshal(resp.Body, &out)
255         return out, err
256 }
257
258 // CopyObjectTo copies the object to another bucket.
259 //
260 // srcObjectKey    source object key. The source bucket is Bucket.BucketName .
261 // destBucketName    target bucket name.
262 // destObjectKey    target object name.
263 // options    copy options, check out parameter options in function CopyObject for more details.
264 //
265 // error    it's nil if no error, otherwise it's an error object.
266 //
267 func (bucket Bucket) CopyObjectTo(destBucketName, destObjectKey, srcObjectKey string, options ...Option) (CopyObjectResult, error) {
268         return bucket.copy(srcObjectKey, destBucketName, destObjectKey, options...)
269 }
270
271 //
272 // CopyObjectFrom copies the object to another bucket.
273 //
274 // srcBucketName    source bucket name.
275 // srcObjectKey    source object name.
276 // destObjectKey    target object name. The target bucket name is Bucket.BucketName.
277 // options    copy options. Check out parameter options in function CopyObject.
278 //
279 // error    it's nil if no error, otherwise it's an error object.
280 //
281 func (bucket Bucket) CopyObjectFrom(srcBucketName, srcObjectKey, destObjectKey string, options ...Option) (CopyObjectResult, error) {
282         destBucketName := bucket.BucketName
283         var out CopyObjectResult
284         srcBucket, err := bucket.Client.Bucket(srcBucketName)
285         if err != nil {
286                 return out, err
287         }
288
289         return srcBucket.copy(srcObjectKey, destBucketName, destObjectKey, options...)
290 }
291
292 func (bucket Bucket) copy(srcObjectKey, destBucketName, destObjectKey string, options ...Option) (CopyObjectResult, error) {
293         var out CopyObjectResult
294
295         //first find version id
296         versionIdKey := "versionId"
297         versionId, _ := FindOption(options, versionIdKey, nil)
298         if versionId == nil {
299                 options = append(options, CopySource(bucket.BucketName, url.QueryEscape(srcObjectKey)))
300         } else {
301                 options = DeleteOption(options, versionIdKey)
302                 options = append(options, CopySourceVersion(bucket.BucketName, url.QueryEscape(srcObjectKey), versionId.(string)))
303         }
304
305         headers := make(map[string]string)
306         err := handleOptions(headers, options)
307         if err != nil {
308                 return out, err
309         }
310         params := map[string]interface{}{}
311         resp, err := bucket.Client.Conn.Do("PUT", destBucketName, destObjectKey, params, headers, nil, 0, nil)
312
313         // get response header
314         respHeader, _ := FindOption(options, responseHeader, nil)
315         if respHeader != nil {
316                 pRespHeader := respHeader.(*http.Header)
317                 *pRespHeader = resp.Headers
318         }
319
320         if err != nil {
321                 return out, err
322         }
323         defer resp.Body.Close()
324
325         err = xmlUnmarshal(resp.Body, &out)
326         return out, err
327 }
328
329 // AppendObject uploads the data in the way of appending an existing or new object.
330 //
331 // AppendObject the parameter appendPosition specifies which postion (in the target object) to append. For the first append (to a non-existing file),
332 // the appendPosition should be 0. The appendPosition in the subsequent calls will be the current object length.
333 // For example, the first appendObject's appendPosition is 0 and it uploaded 65536 bytes data, then the second call's position is 65536.
334 // The response header x-oss-next-append-position after each successful request also specifies the next call's append position (so the caller need not to maintain this information).
335 //
336 // objectKey    the target object to append to.
337 // reader    io.Reader. The read instance for reading the data to append.
338 // appendPosition    the start position to append.
339 // destObjectProperties    the options for the first appending, such as CacheControl, ContentDisposition, ContentEncoding,
340 //                         Expires, ServerSideEncryption, ObjectACL.
341 //
342 // int64    the next append position, it's valid when error is nil.
343 // error    it's nil if no error, otherwise it's an error object.
344 //
345 func (bucket Bucket) AppendObject(objectKey string, reader io.Reader, appendPosition int64, options ...Option) (int64, error) {
346         request := &AppendObjectRequest{
347                 ObjectKey: objectKey,
348                 Reader:    reader,
349                 Position:  appendPosition,
350         }
351
352         result, err := bucket.DoAppendObject(request, options)
353         if err != nil {
354                 return appendPosition, err
355         }
356
357         return result.NextPosition, err
358 }
359
360 // DoAppendObject is the actual API that does the object append.
361 //
362 // request    the request object for appending object.
363 // options    the options for appending object.
364 //
365 // AppendObjectResult    the result object for appending object.
366 // error    it's nil if no error, otherwise it's an error object.
367 //
368 func (bucket Bucket) DoAppendObject(request *AppendObjectRequest, options []Option) (*AppendObjectResult, error) {
369         params := map[string]interface{}{}
370         params["append"] = nil
371         params["position"] = strconv.FormatInt(request.Position, 10)
372         headers := make(map[string]string)
373
374         opts := AddContentType(options, request.ObjectKey)
375         handleOptions(headers, opts)
376
377         var initCRC uint64
378         isCRCSet, initCRCOpt, _ := IsOptionSet(options, initCRC64)
379         if isCRCSet {
380                 initCRC = initCRCOpt.(uint64)
381         }
382
383         listener := GetProgressListener(options)
384
385         handleOptions(headers, opts)
386         resp, err := bucket.Client.Conn.Do("POST", bucket.BucketName, request.ObjectKey, params, headers,
387                 request.Reader, initCRC, listener)
388
389         // get response header
390         respHeader, _ := FindOption(options, responseHeader, nil)
391         if respHeader != nil {
392                 pRespHeader := respHeader.(*http.Header)
393                 *pRespHeader = resp.Headers
394         }
395
396         if err != nil {
397                 return nil, err
398         }
399         defer resp.Body.Close()
400
401         nextPosition, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderOssNextAppendPosition), 10, 64)
402         result := &AppendObjectResult{
403                 NextPosition: nextPosition,
404                 CRC:          resp.ServerCRC,
405         }
406
407         if bucket.GetConfig().IsEnableCRC && isCRCSet {
408                 err = CheckCRC(resp, "AppendObject")
409                 if err != nil {
410                         return result, err
411                 }
412         }
413
414         return result, nil
415 }
416
417 // DeleteObject deletes the object.
418 //
419 // objectKey    the object key to delete.
420 //
421 // error    it's nil if no error, otherwise it's an error object.
422 //
423 func (bucket Bucket) DeleteObject(objectKey string, options ...Option) error {
424         params, _ := GetRawParams(options)
425         resp, err := bucket.do("DELETE", objectKey, params, options, nil, nil)
426         if err != nil {
427                 return err
428         }
429         defer resp.Body.Close()
430         return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
431 }
432
433 // DeleteObjects deletes multiple objects.
434 //
435 // objectKeys    the object keys to delete.
436 // options    the options for deleting objects.
437 //            Supported option is DeleteObjectsQuiet which means it will not return error even deletion failed (not recommended). By default it's not used.
438 //
439 // DeleteObjectsResult    the result object.
440 // error    it's nil if no error, otherwise it's an error object.
441 //
442 func (bucket Bucket) DeleteObjects(objectKeys []string, options ...Option) (DeleteObjectsResult, error) {
443         out := DeleteObjectsResult{}
444         dxml := deleteXML{}
445         for _, key := range objectKeys {
446                 dxml.Objects = append(dxml.Objects, DeleteObject{Key: key})
447         }
448
449         isQuiet, _ := FindOption(options, deleteObjectsQuiet, false)
450         dxml.Quiet = isQuiet.(bool)
451
452         bs, err := xml.Marshal(dxml)
453         if err != nil {
454                 return out, err
455         }
456         buffer := new(bytes.Buffer)
457         buffer.Write(bs)
458
459         contentType := http.DetectContentType(buffer.Bytes())
460         options = append(options, ContentType(contentType))
461         sum := md5.Sum(bs)
462         b64 := base64.StdEncoding.EncodeToString(sum[:])
463         options = append(options, ContentMD5(b64))
464
465         params := map[string]interface{}{}
466         params["delete"] = nil
467         params["encoding-type"] = "url"
468
469         resp, err := bucket.do("POST", "", params, options, buffer, nil)
470         if err != nil {
471                 return out, err
472         }
473         defer resp.Body.Close()
474
475         deletedResult := DeleteObjectVersionsResult{}
476         if !dxml.Quiet {
477                 if err = xmlUnmarshal(resp.Body, &deletedResult); err == nil {
478                         err = decodeDeleteObjectsResult(&deletedResult)
479                 }
480         }
481
482         // Keep compatibility:need convert to struct DeleteObjectsResult
483         out.XMLName = deletedResult.XMLName
484         for _, v := range deletedResult.DeletedObjectsDetail {
485                 out.DeletedObjects = append(out.DeletedObjects, v.Key)
486         }
487
488         return out, err
489 }
490
491 // DeleteObjectVersions deletes multiple object versions.
492 //
493 // objectVersions    the object keys and versions to delete.
494 // options    the options for deleting objects.
495 //            Supported option is DeleteObjectsQuiet which means it will not return error even deletion failed (not recommended). By default it's not used.
496 //
497 // DeleteObjectVersionsResult    the result object.
498 // error    it's nil if no error, otherwise it's an error object.
499 //
500 func (bucket Bucket) DeleteObjectVersions(objectVersions []DeleteObject, options ...Option) (DeleteObjectVersionsResult, error) {
501         out := DeleteObjectVersionsResult{}
502         dxml := deleteXML{}
503         dxml.Objects = objectVersions
504
505         isQuiet, _ := FindOption(options, deleteObjectsQuiet, false)
506         dxml.Quiet = isQuiet.(bool)
507
508         bs, err := xml.Marshal(dxml)
509         if err != nil {
510                 return out, err
511         }
512         buffer := new(bytes.Buffer)
513         buffer.Write(bs)
514
515         contentType := http.DetectContentType(buffer.Bytes())
516         options = append(options, ContentType(contentType))
517         sum := md5.Sum(bs)
518         b64 := base64.StdEncoding.EncodeToString(sum[:])
519         options = append(options, ContentMD5(b64))
520
521         params := map[string]interface{}{}
522         params["delete"] = nil
523         params["encoding-type"] = "url"
524
525         resp, err := bucket.do("POST", "", params, options, buffer, nil)
526         if err != nil {
527                 return out, err
528         }
529         defer resp.Body.Close()
530
531         if !dxml.Quiet {
532                 if err = xmlUnmarshal(resp.Body, &out); err == nil {
533                         err = decodeDeleteObjectsResult(&out)
534                 }
535         }
536         return out, err
537 }
538
539 // IsObjectExist checks if the object exists.
540 //
541 // bool    flag of object's existence (true:exists; false:non-exist) when error is nil.
542 //
543 // error    it's nil if no error, otherwise it's an error object.
544 //
545 func (bucket Bucket) IsObjectExist(objectKey string, options ...Option) (bool, error) {
546         _, err := bucket.GetObjectMeta(objectKey, options...)
547         if err == nil {
548                 return true, nil
549         }
550
551         switch err.(type) {
552         case ServiceError:
553                 if err.(ServiceError).StatusCode == 404 {
554                         return false, nil
555                 }
556         }
557
558         return false, err
559 }
560
561 // ListObjects lists the objects under the current bucket.
562 //
563 // options    it contains all the filters for listing objects.
564 //            It could specify a prefix filter on object keys,  the max keys count to return and the object key marker and the delimiter for grouping object names.
565 //            The key marker means the returned objects' key must be greater than it in lexicographic order.
566 //
567 //            For example, if the bucket has 8 objects, my-object-1, my-object-11, my-object-2, my-object-21,
568 //            my-object-22, my-object-3, my-object-31, my-object-32. If the prefix is my-object-2 (no other filters), then it returns
569 //            my-object-2, my-object-21, my-object-22 three objects. If the marker is my-object-22 (no other filters), then it returns
570 //            my-object-3, my-object-31, my-object-32 three objects. If the max keys is 5, then it returns 5 objects.
571 //            The three filters could be used together to achieve filter and paging functionality.
572 //            If the prefix is the folder name, then it could list all files under this folder (including the files under its subfolders).
573 //            But if the delimiter is specified with '/', then it only returns that folder's files (no subfolder's files). The direct subfolders are in the commonPrefixes properties.
574 //            For example, if the bucket has three objects fun/test.jpg, fun/movie/001.avi, fun/movie/007.avi. And if the prefix is "fun/", then it returns all three objects.
575 //            But if the delimiter is '/', then only "fun/test.jpg" is returned as files and fun/movie/ is returned as common prefix.
576 //
577 //            For common usage scenario, check out sample/list_object.go.
578 //
579 // ListObjectsResult    the return value after operation succeeds (only valid when error is nil).
580 //
581 func (bucket Bucket) ListObjects(options ...Option) (ListObjectsResult, error) {
582         var out ListObjectsResult
583
584         options = append(options, EncodingType("url"))
585         params, err := GetRawParams(options)
586         if err != nil {
587                 return out, err
588         }
589
590         resp, err := bucket.do("GET", "", params, options, nil, nil)
591         if err != nil {
592                 return out, err
593         }
594         defer resp.Body.Close()
595
596         err = xmlUnmarshal(resp.Body, &out)
597         if err != nil {
598                 return out, err
599         }
600
601         err = decodeListObjectsResult(&out)
602         return out, err
603 }
604
605 // Recommend to use ListObjectsV2 to replace ListObjects
606 // ListOListObjectsV2bjects lists the objects under the current bucket.
607 // ListObjectsResultV2    the return value after operation succeeds (only valid when error is nil).
608 func (bucket Bucket) ListObjectsV2(options ...Option) (ListObjectsResultV2, error) {
609         var out ListObjectsResultV2
610
611         options = append(options, EncodingType("url"))
612         options = append(options, ListType(2))
613         params, err := GetRawParams(options)
614         if err != nil {
615                 return out, err
616         }
617
618         resp, err := bucket.do("GET", "", params, options, nil, nil)
619         if err != nil {
620                 return out, err
621         }
622         defer resp.Body.Close()
623
624         err = xmlUnmarshal(resp.Body, &out)
625         if err != nil {
626                 return out, err
627         }
628
629         err = decodeListObjectsResultV2(&out)
630         return out, err
631 }
632
633 // ListObjectVersions lists objects of all versions under the current bucket.
634 func (bucket Bucket) ListObjectVersions(options ...Option) (ListObjectVersionsResult, error) {
635         var out ListObjectVersionsResult
636
637         options = append(options, EncodingType("url"))
638         params, err := GetRawParams(options)
639         if err != nil {
640                 return out, err
641         }
642         params["versions"] = nil
643
644         resp, err := bucket.do("GET", "", params, options, nil, nil)
645         if err != nil {
646                 return out, err
647         }
648         defer resp.Body.Close()
649
650         err = xmlUnmarshal(resp.Body, &out)
651         if err != nil {
652                 return out, err
653         }
654
655         err = decodeListObjectVersionsResult(&out)
656         return out, err
657 }
658
659 // SetObjectMeta sets the metadata of the Object.
660 //
661 // objectKey    object
662 // options    options for setting the metadata. The valid options are CacheControl, ContentDisposition, ContentEncoding, Expires,
663 //            ServerSideEncryption, and custom metadata.
664 //
665 // error    it's nil if no error, otherwise it's an error object.
666 //
667 func (bucket Bucket) SetObjectMeta(objectKey string, options ...Option) error {
668         options = append(options, MetadataDirective(MetaReplace))
669         _, err := bucket.CopyObject(objectKey, objectKey, options...)
670         return err
671 }
672
673 // GetObjectDetailedMeta gets the object's detailed metadata
674 //
675 // objectKey    object key.
676 // options    the constraints of the object. Only when the object meets the requirements this method will return the metadata. Otherwise returns error. Valid options are IfModifiedSince, IfUnmodifiedSince,
677 //            IfMatch, IfNoneMatch. For more details check out https://help.aliyun.com/document_detail/oss/api-reference/object/HeadObject.html
678 //
679 // http.Header    object meta when error is nil.
680 // error    it's nil if no error, otherwise it's an error object.
681 //
682 func (bucket Bucket) GetObjectDetailedMeta(objectKey string, options ...Option) (http.Header, error) {
683         params, _ := GetRawParams(options)
684         resp, err := bucket.do("HEAD", objectKey, params, options, nil, nil)
685         if err != nil {
686                 return nil, err
687         }
688         defer resp.Body.Close()
689
690         return resp.Headers, nil
691 }
692
693 // GetObjectMeta gets object metadata.
694 //
695 // GetObjectMeta is more lightweight than GetObjectDetailedMeta as it only returns basic metadata including ETag
696 // size, LastModified. The size information is in the HTTP header Content-Length.
697 //
698 // objectKey    object key
699 //
700 // http.Header    the object's metadata, valid when error is nil.
701 // error    it's nil if no error, otherwise it's an error object.
702 //
703 func (bucket Bucket) GetObjectMeta(objectKey string, options ...Option) (http.Header, error) {
704         params, _ := GetRawParams(options)
705         params["objectMeta"] = nil
706         //resp, err := bucket.do("GET", objectKey, "?objectMeta", "", nil, nil, nil)
707         resp, err := bucket.do("HEAD", objectKey, params, options, nil, nil)
708         if err != nil {
709                 return nil, err
710         }
711         defer resp.Body.Close()
712
713         return resp.Headers, nil
714 }
715
716 // SetObjectACL updates the object's ACL.
717 //
718 // Only the bucket's owner could update object's ACL which priority is higher than bucket's ACL.
719 // For example, if the bucket ACL is private and object's ACL is public-read-write.
720 // Then object's ACL is used and it means all users could read or write that object.
721 // When the object's ACL is not set, then bucket's ACL is used as the object's ACL.
722 //
723 // Object read operations include GetObject, HeadObject, CopyObject and UploadPartCopy on the source object;
724 // Object write operations include PutObject, PostObject, AppendObject, DeleteObject, DeleteMultipleObjects,
725 // CompleteMultipartUpload and CopyObject on target object.
726 //
727 // objectKey    the target object key (to set the ACL on)
728 // objectAcl    object ACL. Valid options are PrivateACL, PublicReadACL, PublicReadWriteACL.
729 //
730 // error    it's nil if no error, otherwise it's an error object.
731 //
732 func (bucket Bucket) SetObjectACL(objectKey string, objectACL ACLType, options ...Option) error {
733         options = append(options, ObjectACL(objectACL))
734         params, _ := GetRawParams(options)
735         params["acl"] = nil
736         resp, err := bucket.do("PUT", objectKey, params, options, nil, nil)
737         if err != nil {
738                 return err
739         }
740         defer resp.Body.Close()
741         return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
742 }
743
744 // GetObjectACL gets object's ACL
745 //
746 // objectKey    the object to get ACL from.
747 //
748 // GetObjectACLResult    the result object when error is nil. GetObjectACLResult.Acl is the object ACL.
749 // error    it's nil if no error, otherwise it's an error object.
750 //
751 func (bucket Bucket) GetObjectACL(objectKey string, options ...Option) (GetObjectACLResult, error) {
752         var out GetObjectACLResult
753         params, _ := GetRawParams(options)
754         params["acl"] = nil
755         resp, err := bucket.do("GET", objectKey, params, options, nil, nil)
756         if err != nil {
757                 return out, err
758         }
759         defer resp.Body.Close()
760
761         err = xmlUnmarshal(resp.Body, &out)
762         return out, err
763 }
764
765 // PutSymlink creates a symlink (to point to an existing object)
766 //
767 // Symlink cannot point to another symlink.
768 // When creating a symlink, it does not check the existence of the target file, and does not check if the target file is symlink.
769 // Neither it checks the caller's permission on the target file. All these checks are deferred to the actual GetObject call via this symlink.
770 // If trying to add an existing file, as long as the caller has the write permission, the existing one will be overwritten.
771 // If the x-oss-meta- is specified, it will be added as the metadata of the symlink file.
772 //
773 // symObjectKey    the symlink object's key.
774 // targetObjectKey    the target object key to point to.
775 //
776 // error    it's nil if no error, otherwise it's an error object.
777 //
778 func (bucket Bucket) PutSymlink(symObjectKey string, targetObjectKey string, options ...Option) error {
779         options = append(options, symlinkTarget(url.QueryEscape(targetObjectKey)))
780         params, _ := GetRawParams(options)
781         params["symlink"] = nil
782         resp, err := bucket.do("PUT", symObjectKey, params, options, nil, nil)
783         if err != nil {
784                 return err
785         }
786         defer resp.Body.Close()
787         return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
788 }
789
790 // GetSymlink gets the symlink object with the specified key.
791 // If the symlink object does not exist, returns 404.
792 //
793 // objectKey    the symlink object's key.
794 //
795 // error    it's nil if no error, otherwise it's an error object.
796 //          When error is nil, the target file key is in the X-Oss-Symlink-Target header of the returned object.
797 //
798 func (bucket Bucket) GetSymlink(objectKey string, options ...Option) (http.Header, error) {
799         params, _ := GetRawParams(options)
800         params["symlink"] = nil
801         resp, err := bucket.do("GET", objectKey, params, options, nil, nil)
802         if err != nil {
803                 return nil, err
804         }
805         defer resp.Body.Close()
806
807         targetObjectKey := resp.Headers.Get(HTTPHeaderOssSymlinkTarget)
808         targetObjectKey, err = url.QueryUnescape(targetObjectKey)
809         if err != nil {
810                 return resp.Headers, err
811         }
812         resp.Headers.Set(HTTPHeaderOssSymlinkTarget, targetObjectKey)
813         return resp.Headers, err
814 }
815
816 // RestoreObject restores the object from the archive storage.
817 //
818 // An archive object is in cold status by default and it cannot be accessed.
819 // When restore is called on the cold object, it will become available for access after some time.
820 // If multiple restores are called on the same file when the object is being restored, server side does nothing for additional calls but returns success.
821 // By default, the restored object is available for access for one day. After that it will be unavailable again.
822 // But if another RestoreObject are called after the file is restored, then it will extend one day's access time of that object, up to 7 days.
823 //
824 // objectKey    object key to restore.
825 //
826 // error    it's nil if no error, otherwise it's an error object.
827 //
828 func (bucket Bucket) RestoreObject(objectKey string, options ...Option) error {
829         params, _ := GetRawParams(options)
830         params["restore"] = nil
831         resp, err := bucket.do("POST", objectKey, params, options, nil, nil)
832         if err != nil {
833                 return err
834         }
835         defer resp.Body.Close()
836         return CheckRespCode(resp.StatusCode, []int{http.StatusOK, http.StatusAccepted})
837 }
838
839 // RestoreObjectDetail support more features than RestoreObject
840 func (bucket Bucket) RestoreObjectDetail(objectKey string, restoreConfig RestoreConfiguration, options ...Option) error {
841         if restoreConfig.Tier == "" {
842                 // Expedited, Standard, Bulk
843                 restoreConfig.Tier = string(RestoreStandard)
844         }
845
846         if restoreConfig.Days == 0 {
847                 restoreConfig.Days = 1
848         }
849
850         bs, err := xml.Marshal(restoreConfig)
851         if err != nil {
852                 return err
853         }
854
855         buffer := new(bytes.Buffer)
856         buffer.Write(bs)
857
858         contentType := http.DetectContentType(buffer.Bytes())
859         options = append(options, ContentType(contentType))
860
861         params, _ := GetRawParams(options)
862         params["restore"] = nil
863
864         resp, err := bucket.do("POST", objectKey, params, options, buffer, nil)
865         if err != nil {
866                 return err
867         }
868         defer resp.Body.Close()
869         return CheckRespCode(resp.StatusCode, []int{http.StatusOK, http.StatusAccepted})
870 }
871
872 // RestoreObjectXML support more features than RestoreObject
873 func (bucket Bucket) RestoreObjectXML(objectKey, configXML string, options ...Option) error {
874         buffer := new(bytes.Buffer)
875         buffer.Write([]byte(configXML))
876
877         contentType := http.DetectContentType(buffer.Bytes())
878         options = append(options, ContentType(contentType))
879
880         params, _ := GetRawParams(options)
881         params["restore"] = nil
882
883         resp, err := bucket.do("POST", objectKey, params, options, buffer, nil)
884         if err != nil {
885                 return err
886         }
887         defer resp.Body.Close()
888         return CheckRespCode(resp.StatusCode, []int{http.StatusOK, http.StatusAccepted})
889 }
890
891 // SignURL signs the URL. Users could access the object directly with this URL without getting the AK.
892 //
893 // objectKey    the target object to sign.
894 // signURLConfig    the configuration for the signed URL
895 //
896 // string    returns the signed URL, when error is nil.
897 // error    it's nil if no error, otherwise it's an error object.
898 //
899 func (bucket Bucket) SignURL(objectKey string, method HTTPMethod, expiredInSec int64, options ...Option) (string, error) {
900         if expiredInSec < 0 {
901                 return "", fmt.Errorf("invalid expires: %d, expires must bigger than 0", expiredInSec)
902         }
903         expiration := time.Now().Unix() + expiredInSec
904
905         params, err := GetRawParams(options)
906         if err != nil {
907                 return "", err
908         }
909
910         headers := make(map[string]string)
911         err = handleOptions(headers, options)
912         if err != nil {
913                 return "", err
914         }
915
916         return bucket.Client.Conn.signURL(method, bucket.BucketName, objectKey, expiration, params, headers), nil
917 }
918
919 // PutObjectWithURL uploads an object with the URL. If the object exists, it will be overwritten.
920 // PutObjectWithURL It will not generate minetype according to the key name.
921 //
922 // signedURL    signed URL.
923 // reader    io.Reader the read instance for reading the data for the upload.
924 // options    the options for uploading the data. The valid options are CacheControl, ContentDisposition, ContentEncoding,
925 //            Expires, ServerSideEncryption, ObjectACL and custom metadata. Check out the following link for details:
926 //            https://help.aliyun.com/document_detail/oss/api-reference/object/PutObject.html
927 //
928 // error    it's nil if no error, otherwise it's an error object.
929 //
930 func (bucket Bucket) PutObjectWithURL(signedURL string, reader io.Reader, options ...Option) error {
931         resp, err := bucket.DoPutObjectWithURL(signedURL, reader, options)
932         if err != nil {
933                 return err
934         }
935         defer resp.Body.Close()
936
937         return err
938 }
939
940 // PutObjectFromFileWithURL uploads an object from a local file with the signed URL.
941 // PutObjectFromFileWithURL It does not generate mimetype according to object key's name or the local file name.
942 //
943 // signedURL    the signed URL.
944 // filePath    local file path, such as dirfile.txt, for uploading.
945 // options    options for uploading, same as the options in PutObject function.
946 //
947 // error    it's nil if no error, otherwise it's an error object.
948 //
949 func (bucket Bucket) PutObjectFromFileWithURL(signedURL, filePath string, options ...Option) error {
950         fd, err := os.Open(filePath)
951         if err != nil {
952                 return err
953         }
954         defer fd.Close()
955
956         resp, err := bucket.DoPutObjectWithURL(signedURL, fd, options)
957         if err != nil {
958                 return err
959         }
960         defer resp.Body.Close()
961
962         return err
963 }
964
965 // DoPutObjectWithURL is the actual API that does the upload with URL work(internal for SDK)
966 //
967 // signedURL    the signed URL.
968 // reader    io.Reader the read instance for getting the data to upload.
969 // options    options for uploading.
970 //
971 // Response    the response object which contains the HTTP response.
972 // error    it's nil if no error, otherwise it's an error object.
973 //
974 func (bucket Bucket) DoPutObjectWithURL(signedURL string, reader io.Reader, options []Option) (*Response, error) {
975         listener := GetProgressListener(options)
976
977         params := map[string]interface{}{}
978         resp, err := bucket.doURL("PUT", signedURL, params, options, reader, listener)
979         if err != nil {
980                 return nil, err
981         }
982
983         if bucket.GetConfig().IsEnableCRC {
984                 err = CheckCRC(resp, "DoPutObjectWithURL")
985                 if err != nil {
986                         return resp, err
987                 }
988         }
989
990         err = CheckRespCode(resp.StatusCode, []int{http.StatusOK})
991
992         return resp, err
993 }
994
995 // GetObjectWithURL downloads the object and returns the reader instance,  with the signed URL.
996 //
997 // signedURL    the signed URL.
998 // options    options for downloading the object. Valid options are IfModifiedSince, IfUnmodifiedSince, IfMatch,
999 //            IfNoneMatch, AcceptEncoding. For more information, check out the following link:
1000 //            https://help.aliyun.com/document_detail/oss/api-reference/object/GetObject.html
1001 //
1002 // io.ReadCloser    the reader object for getting the data from response. It needs be closed after the usage. It's only valid when error is nil.
1003 // error    it's nil if no error, otherwise it's an error object.
1004 //
1005 func (bucket Bucket) GetObjectWithURL(signedURL string, options ...Option) (io.ReadCloser, error) {
1006         result, err := bucket.DoGetObjectWithURL(signedURL, options)
1007         if err != nil {
1008                 return nil, err
1009         }
1010         return result.Response, nil
1011 }
1012
1013 // GetObjectToFileWithURL downloads the object into a local file with the signed URL.
1014 //
1015 // signedURL    the signed URL
1016 // filePath    the local file path to download to.
1017 // options    the options for downloading object. Check out the parameter options in function GetObject for the reference.
1018 //
1019 // error    it's nil if no error, otherwise it's an error object.
1020 //
1021 func (bucket Bucket) GetObjectToFileWithURL(signedURL, filePath string, options ...Option) error {
1022         tempFilePath := filePath + TempFileSuffix
1023
1024         // Get the object's content
1025         result, err := bucket.DoGetObjectWithURL(signedURL, options)
1026         if err != nil {
1027                 return err
1028         }
1029         defer result.Response.Close()
1030
1031         // If the file does not exist, create one. If exists, then overwrite it.
1032         fd, err := os.OpenFile(tempFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, FilePermMode)
1033         if err != nil {
1034                 return err
1035         }
1036
1037         // Save the data to the file.
1038         _, err = io.Copy(fd, result.Response.Body)
1039         fd.Close()
1040         if err != nil {
1041                 return err
1042         }
1043
1044         // Compare the CRC value. If CRC values do not match, return error.
1045         hasRange, _, _ := IsOptionSet(options, HTTPHeaderRange)
1046         encodeOpt, _ := FindOption(options, HTTPHeaderAcceptEncoding, nil)
1047         acceptEncoding := ""
1048         if encodeOpt != nil {
1049                 acceptEncoding = encodeOpt.(string)
1050         }
1051
1052         if bucket.GetConfig().IsEnableCRC && !hasRange && acceptEncoding != "gzip" {
1053                 result.Response.ClientCRC = result.ClientCRC.Sum64()
1054                 err = CheckCRC(result.Response, "GetObjectToFileWithURL")
1055                 if err != nil {
1056                         os.Remove(tempFilePath)
1057                         return err
1058                 }
1059         }
1060
1061         return os.Rename(tempFilePath, filePath)
1062 }
1063
1064 // DoGetObjectWithURL is the actual API that downloads the file with the signed URL.
1065 //
1066 // signedURL    the signed URL.
1067 // options    the options for getting object. Check out parameter options in GetObject for the reference.
1068 //
1069 // GetObjectResult    the result object when the error is nil.
1070 // error    it's nil if no error, otherwise it's an error object.
1071 //
1072 func (bucket Bucket) DoGetObjectWithURL(signedURL string, options []Option) (*GetObjectResult, error) {
1073         params, _ := GetRawParams(options)
1074         resp, err := bucket.doURL("GET", signedURL, params, options, nil, nil)
1075         if err != nil {
1076                 return nil, err
1077         }
1078
1079         result := &GetObjectResult{
1080                 Response: resp,
1081         }
1082
1083         // CRC
1084         var crcCalc hash.Hash64
1085         hasRange, _, _ := IsOptionSet(options, HTTPHeaderRange)
1086         if bucket.GetConfig().IsEnableCRC && !hasRange {
1087                 crcCalc = crc64.New(CrcTable())
1088                 result.ServerCRC = resp.ServerCRC
1089                 result.ClientCRC = crcCalc
1090         }
1091
1092         // Progress
1093         listener := GetProgressListener(options)
1094
1095         contentLen, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderContentLength), 10, 64)
1096         resp.Body = TeeReader(resp.Body, crcCalc, contentLen, listener, nil)
1097
1098         return result, nil
1099 }
1100
1101 //
1102 // ProcessObject apply process on the specified image file.
1103 //
1104 // The supported process includes resize, rotate, crop, watermark, format,
1105 // udf, customized style, etc.
1106 //
1107 //
1108 // objectKey    object key to process.
1109 // process      process string, such as "image/resize,w_100|sys/saveas,o_dGVzdC5qcGc,b_dGVzdA"
1110 //
1111 // error    it's nil if no error, otherwise it's an error object.
1112 //
1113 func (bucket Bucket) ProcessObject(objectKey string, process string, options ...Option) (ProcessObjectResult, error) {
1114         var out ProcessObjectResult
1115         params, _ := GetRawParams(options)
1116         params["x-oss-process"] = nil
1117         processData := fmt.Sprintf("%v=%v", "x-oss-process", process)
1118         data := strings.NewReader(processData)
1119         resp, err := bucket.do("POST", objectKey, params, nil, data, nil)
1120         if err != nil {
1121                 return out, err
1122         }
1123         defer resp.Body.Close()
1124
1125         err = jsonUnmarshal(resp.Body, &out)
1126         return out, err
1127 }
1128
1129 //
1130 // PutObjectTagging add tagging to object
1131 //
1132 // objectKey  object key to add tagging
1133 // tagging    tagging to be added
1134 //
1135 // error        nil if success, otherwise error
1136 //
1137 func (bucket Bucket) PutObjectTagging(objectKey string, tagging Tagging, options ...Option) error {
1138         bs, err := xml.Marshal(tagging)
1139         if err != nil {
1140                 return err
1141         }
1142
1143         buffer := new(bytes.Buffer)
1144         buffer.Write(bs)
1145
1146         params, _ := GetRawParams(options)
1147         params["tagging"] = nil
1148         resp, err := bucket.do("PUT", objectKey, params, options, buffer, nil)
1149         if err != nil {
1150                 return err
1151         }
1152         defer resp.Body.Close()
1153
1154         return nil
1155 }
1156
1157 //
1158 // GetObjectTagging get tagging of the object
1159 //
1160 // objectKey  object key to get tagging
1161 //
1162 // Tagging
1163 // error      nil if success, otherwise error
1164
1165 func (bucket Bucket) GetObjectTagging(objectKey string, options ...Option) (GetObjectTaggingResult, error) {
1166         var out GetObjectTaggingResult
1167         params, _ := GetRawParams(options)
1168         params["tagging"] = nil
1169
1170         resp, err := bucket.do("GET", objectKey, params, options, nil, nil)
1171         if err != nil {
1172                 return out, err
1173         }
1174         defer resp.Body.Close()
1175
1176         err = xmlUnmarshal(resp.Body, &out)
1177         return out, err
1178 }
1179
1180 //
1181 // DeleteObjectTagging delete object taggging
1182 //
1183 // objectKey  object key to delete tagging
1184 //
1185 // error      nil if success, otherwise error
1186 //
1187 func (bucket Bucket) DeleteObjectTagging(objectKey string, options ...Option) error {
1188         params, _ := GetRawParams(options)
1189         params["tagging"] = nil
1190
1191         if objectKey == "" {
1192                 return fmt.Errorf("invalid argument: object name is empty")
1193         }
1194
1195         resp, err := bucket.do("DELETE", objectKey, params, options, nil, nil)
1196         if err != nil {
1197                 return err
1198         }
1199         defer resp.Body.Close()
1200
1201         return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
1202 }
1203
1204 func (bucket Bucket) OptionsMethod(objectKey string, options ...Option) (http.Header, error) {
1205         var out http.Header
1206         resp, err := bucket.do("OPTIONS", objectKey, nil, options, nil, nil)
1207         if err != nil {
1208                 return out, err
1209         }
1210         defer resp.Body.Close()
1211         out = resp.Headers
1212         return out, nil
1213 }
1214
1215 // public
1216 func (bucket Bucket) Do(method, objectName string, params map[string]interface{}, options []Option,
1217         data io.Reader, listener ProgressListener) (*Response, error) {
1218         return bucket.do(method, objectName, params, options, data, listener)
1219 }
1220
1221 // Private
1222 func (bucket Bucket) do(method, objectName string, params map[string]interface{}, options []Option,
1223         data io.Reader, listener ProgressListener) (*Response, error) {
1224         headers := make(map[string]string)
1225         err := handleOptions(headers, options)
1226         if err != nil {
1227                 return nil, err
1228         }
1229
1230         err = CheckBucketName(bucket.BucketName)
1231         if len(bucket.BucketName) > 0 && err != nil {
1232                 return nil, err
1233         }
1234
1235         resp, err := bucket.Client.Conn.Do(method, bucket.BucketName, objectName,
1236                 params, headers, data, 0, listener)
1237
1238         // get response header
1239         respHeader, _ := FindOption(options, responseHeader, nil)
1240         if respHeader != nil && resp != nil {
1241                 pRespHeader := respHeader.(*http.Header)
1242                 *pRespHeader = resp.Headers
1243         }
1244
1245         return resp, err
1246 }
1247
1248 func (bucket Bucket) doURL(method HTTPMethod, signedURL string, params map[string]interface{}, options []Option,
1249         data io.Reader, listener ProgressListener) (*Response, error) {
1250         headers := make(map[string]string)
1251         err := handleOptions(headers, options)
1252         if err != nil {
1253                 return nil, err
1254         }
1255
1256         resp, err := bucket.Client.Conn.DoURL(method, signedURL, headers, data, 0, listener)
1257
1258         // get response header
1259         respHeader, _ := FindOption(options, responseHeader, nil)
1260         if respHeader != nil {
1261                 pRespHeader := respHeader.(*http.Header)
1262                 *pRespHeader = resp.Headers
1263         }
1264
1265         return resp, err
1266 }
1267
1268 func (bucket Bucket) GetConfig() *Config {
1269         return bucket.Client.Config
1270 }
1271
1272 func AddContentType(options []Option, keys ...string) []Option {
1273         typ := TypeByExtension("")
1274         for _, key := range keys {
1275                 typ = TypeByExtension(key)
1276                 if typ != "" {
1277                         break
1278                 }
1279         }
1280
1281         if typ == "" {
1282                 typ = "application/octet-stream"
1283         }
1284
1285         opts := []Option{ContentType(typ)}
1286         opts = append(opts, options...)
1287
1288         return opts
1289 }