20 // Bucket implements the operations of object.
26 // PutObject creates a new object and it will overwrite the original one if it exists already.
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
34 // error it's nil if no error, otherwise it's an error object.
36 func (bucket Bucket) PutObject(objectKey string, reader io.Reader, options ...Option) error {
37 opts := AddContentType(options, objectKey)
39 request := &PutObjectRequest{
43 resp, err := bucket.DoPutObject(request, opts)
47 defer resp.Body.Close()
52 // PutObjectFromFile creates a new object from the local file.
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.
58 // error it's nil if no error, otherwise it's an error object.
60 func (bucket Bucket) PutObjectFromFile(objectKey, filePath string, options ...Option) error {
61 fd, err := os.Open(filePath)
67 opts := AddContentType(options, filePath, objectKey)
69 request := &PutObjectRequest{
73 resp, err := bucket.DoPutObject(request, opts)
77 defer resp.Body.Close()
82 // DoPutObject does the actual upload work.
84 // request the request instance for uploading an object.
85 // options the options for uploading an object.
87 // Response the response from OSS.
88 // error it's nil if no error, otherwise it's an error object.
90 func (bucket Bucket) DoPutObject(request *PutObjectRequest, options []Option) (*Response, error) {
91 isOptSet, _, _ := IsOptionSet(options, HTTPHeaderContentType)
93 options = AddContentType(options, request.ObjectKey)
96 listener := GetProgressListener(options)
98 params := map[string]interface{}{}
99 resp, err := bucket.do("PUT", request.ObjectKey, params, options, request.Reader, listener)
104 if bucket.GetConfig().IsEnableCRC {
105 err = CheckCRC(resp, "DoPutObject")
111 err = CheckRespCode(resp.StatusCode, []int{http.StatusOK})
116 // GetObject downloads the object.
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
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.
126 func (bucket Bucket) GetObject(objectKey string, options ...Option) (io.ReadCloser, error) {
127 result, err := bucket.DoGetObject(&GetObjectRequest{objectKey}, options)
132 return result.Response, nil
135 // GetObjectToFile downloads the data to a local file.
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.
141 // error it's nil if no error, otherwise it's an error object.
143 func (bucket Bucket) GetObjectToFile(objectKey, filePath string, options ...Option) error {
144 tempFilePath := filePath + TempFileSuffix
146 // Calls the API to actually download the object. Returns the result instance.
147 result, err := bucket.DoGetObject(&GetObjectRequest{objectKey}, options)
151 defer result.Response.Close()
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)
159 // Copy the data to the local file path.
160 _, err = io.Copy(fd, result.Response.Body)
166 // Compares the CRC value
167 hasRange, _, _ := IsOptionSet(options, HTTPHeaderRange)
168 encodeOpt, _ := FindOption(options, HTTPHeaderAcceptEncoding, nil)
170 if encodeOpt != nil {
171 acceptEncoding = encodeOpt.(string)
173 if bucket.GetConfig().IsEnableCRC && !hasRange && acceptEncoding != "gzip" {
174 result.Response.ClientCRC = result.ClientCRC.Sum64()
175 err = CheckCRC(result.Response, "GetObjectToFile")
177 os.Remove(tempFilePath)
182 return os.Rename(tempFilePath, filePath)
185 // DoGetObject is the actual API that gets the object. It's the internal function called by other public APIs.
187 // request the request to download the object.
188 // options the options for downloading the file. Checks out the parameter options in method GetObject.
190 // GetObjectResult the result instance of getting the object.
191 // error it's nil if no error, otherwise it's an error object.
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)
200 result := &GetObjectResult{
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
214 listener := GetProgressListener(options)
216 contentLen, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderContentLength), 10, 64)
217 resp.Body = TeeReader(resp.Body, crcCalc, contentLen, listener, nil)
222 // CopyObject copies the object inside the bucket.
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
232 // error it's nil if no error, otherwise it's an error object.
234 func (bucket Bucket) CopyObject(srcObjectKey, destObjectKey string, options ...Option) (CopyObjectResult, error) {
235 var out CopyObjectResult
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)))
243 options = DeleteOption(options, versionIdKey)
244 options = append(options, CopySourceVersion(bucket.BucketName, url.QueryEscape(srcObjectKey), versionId.(string)))
247 params := map[string]interface{}{}
248 resp, err := bucket.do("PUT", destObjectKey, params, options, nil, nil)
252 defer resp.Body.Close()
254 err = xmlUnmarshal(resp.Body, &out)
258 // CopyObjectTo copies the object to another bucket.
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.
265 // error it's nil if no error, otherwise it's an error object.
267 func (bucket Bucket) CopyObjectTo(destBucketName, destObjectKey, srcObjectKey string, options ...Option) (CopyObjectResult, error) {
268 return bucket.copy(srcObjectKey, destBucketName, destObjectKey, options...)
272 // CopyObjectFrom copies the object to another bucket.
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.
279 // error it's nil if no error, otherwise it's an error object.
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)
289 return srcBucket.copy(srcObjectKey, destBucketName, destObjectKey, options...)
292 func (bucket Bucket) copy(srcObjectKey, destBucketName, destObjectKey string, options ...Option) (CopyObjectResult, error) {
293 var out CopyObjectResult
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)))
301 options = DeleteOption(options, versionIdKey)
302 options = append(options, CopySourceVersion(bucket.BucketName, url.QueryEscape(srcObjectKey), versionId.(string)))
305 headers := make(map[string]string)
306 err := handleOptions(headers, options)
310 params := map[string]interface{}{}
311 resp, err := bucket.Client.Conn.Do("PUT", destBucketName, destObjectKey, params, headers, nil, 0, nil)
313 // get response header
314 respHeader, _ := FindOption(options, responseHeader, nil)
315 if respHeader != nil {
316 pRespHeader := respHeader.(*http.Header)
317 *pRespHeader = resp.Headers
323 defer resp.Body.Close()
325 err = xmlUnmarshal(resp.Body, &out)
329 // AppendObject uploads the data in the way of appending an existing or new object.
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).
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.
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.
345 func (bucket Bucket) AppendObject(objectKey string, reader io.Reader, appendPosition int64, options ...Option) (int64, error) {
346 request := &AppendObjectRequest{
347 ObjectKey: objectKey,
349 Position: appendPosition,
352 result, err := bucket.DoAppendObject(request, options)
354 return appendPosition, err
357 return result.NextPosition, err
360 // DoAppendObject is the actual API that does the object append.
362 // request the request object for appending object.
363 // options the options for appending object.
365 // AppendObjectResult the result object for appending object.
366 // error it's nil if no error, otherwise it's an error object.
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)
374 opts := AddContentType(options, request.ObjectKey)
375 handleOptions(headers, opts)
378 isCRCSet, initCRCOpt, _ := IsOptionSet(options, initCRC64)
380 initCRC = initCRCOpt.(uint64)
383 listener := GetProgressListener(options)
385 handleOptions(headers, opts)
386 resp, err := bucket.Client.Conn.Do("POST", bucket.BucketName, request.ObjectKey, params, headers,
387 request.Reader, initCRC, listener)
389 // get response header
390 respHeader, _ := FindOption(options, responseHeader, nil)
391 if respHeader != nil {
392 pRespHeader := respHeader.(*http.Header)
393 *pRespHeader = resp.Headers
399 defer resp.Body.Close()
401 nextPosition, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderOssNextAppendPosition), 10, 64)
402 result := &AppendObjectResult{
403 NextPosition: nextPosition,
407 if bucket.GetConfig().IsEnableCRC && isCRCSet {
408 err = CheckCRC(resp, "AppendObject")
417 // DeleteObject deletes the object.
419 // objectKey the object key to delete.
421 // error it's nil if no error, otherwise it's an error object.
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)
429 defer resp.Body.Close()
430 return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
433 // DeleteObjects deletes multiple objects.
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.
439 // DeleteObjectsResult the result object.
440 // error it's nil if no error, otherwise it's an error object.
442 func (bucket Bucket) DeleteObjects(objectKeys []string, options ...Option) (DeleteObjectsResult, error) {
443 out := DeleteObjectsResult{}
445 for _, key := range objectKeys {
446 dxml.Objects = append(dxml.Objects, DeleteObject{Key: key})
449 isQuiet, _ := FindOption(options, deleteObjectsQuiet, false)
450 dxml.Quiet = isQuiet.(bool)
452 bs, err := xml.Marshal(dxml)
456 buffer := new(bytes.Buffer)
459 contentType := http.DetectContentType(buffer.Bytes())
460 options = append(options, ContentType(contentType))
462 b64 := base64.StdEncoding.EncodeToString(sum[:])
463 options = append(options, ContentMD5(b64))
465 params := map[string]interface{}{}
466 params["delete"] = nil
467 params["encoding-type"] = "url"
469 resp, err := bucket.do("POST", "", params, options, buffer, nil)
473 defer resp.Body.Close()
475 deletedResult := DeleteObjectVersionsResult{}
477 if err = xmlUnmarshal(resp.Body, &deletedResult); err == nil {
478 err = decodeDeleteObjectsResult(&deletedResult)
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)
491 // DeleteObjectVersions deletes multiple object versions.
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.
497 // DeleteObjectVersionsResult the result object.
498 // error it's nil if no error, otherwise it's an error object.
500 func (bucket Bucket) DeleteObjectVersions(objectVersions []DeleteObject, options ...Option) (DeleteObjectVersionsResult, error) {
501 out := DeleteObjectVersionsResult{}
503 dxml.Objects = objectVersions
505 isQuiet, _ := FindOption(options, deleteObjectsQuiet, false)
506 dxml.Quiet = isQuiet.(bool)
508 bs, err := xml.Marshal(dxml)
512 buffer := new(bytes.Buffer)
515 contentType := http.DetectContentType(buffer.Bytes())
516 options = append(options, ContentType(contentType))
518 b64 := base64.StdEncoding.EncodeToString(sum[:])
519 options = append(options, ContentMD5(b64))
521 params := map[string]interface{}{}
522 params["delete"] = nil
523 params["encoding-type"] = "url"
525 resp, err := bucket.do("POST", "", params, options, buffer, nil)
529 defer resp.Body.Close()
532 if err = xmlUnmarshal(resp.Body, &out); err == nil {
533 err = decodeDeleteObjectsResult(&out)
539 // IsObjectExist checks if the object exists.
541 // bool flag of object's existence (true:exists; false:non-exist) when error is nil.
543 // error it's nil if no error, otherwise it's an error object.
545 func (bucket Bucket) IsObjectExist(objectKey string, options ...Option) (bool, error) {
546 _, err := bucket.GetObjectMeta(objectKey, options...)
553 if err.(ServiceError).StatusCode == 404 {
561 // ListObjects lists the objects under the current bucket.
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.
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.
577 // For common usage scenario, check out sample/list_object.go.
579 // ListObjectsResult the return value after operation succeeds (only valid when error is nil).
581 func (bucket Bucket) ListObjects(options ...Option) (ListObjectsResult, error) {
582 var out ListObjectsResult
584 options = append(options, EncodingType("url"))
585 params, err := GetRawParams(options)
590 resp, err := bucket.do("GET", "", params, options, nil, nil)
594 defer resp.Body.Close()
596 err = xmlUnmarshal(resp.Body, &out)
601 err = decodeListObjectsResult(&out)
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
611 options = append(options, EncodingType("url"))
612 options = append(options, ListType(2))
613 params, err := GetRawParams(options)
618 resp, err := bucket.do("GET", "", params, options, nil, nil)
622 defer resp.Body.Close()
624 err = xmlUnmarshal(resp.Body, &out)
629 err = decodeListObjectsResultV2(&out)
633 // ListObjectVersions lists objects of all versions under the current bucket.
634 func (bucket Bucket) ListObjectVersions(options ...Option) (ListObjectVersionsResult, error) {
635 var out ListObjectVersionsResult
637 options = append(options, EncodingType("url"))
638 params, err := GetRawParams(options)
642 params["versions"] = nil
644 resp, err := bucket.do("GET", "", params, options, nil, nil)
648 defer resp.Body.Close()
650 err = xmlUnmarshal(resp.Body, &out)
655 err = decodeListObjectVersionsResult(&out)
659 // SetObjectMeta sets the metadata of the Object.
662 // options options for setting the metadata. The valid options are CacheControl, ContentDisposition, ContentEncoding, Expires,
663 // ServerSideEncryption, and custom metadata.
665 // error it's nil if no error, otherwise it's an error object.
667 func (bucket Bucket) SetObjectMeta(objectKey string, options ...Option) error {
668 options = append(options, MetadataDirective(MetaReplace))
669 _, err := bucket.CopyObject(objectKey, objectKey, options...)
673 // GetObjectDetailedMeta gets the object's detailed metadata
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
679 // http.Header object meta when error is nil.
680 // error it's nil if no error, otherwise it's an error object.
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)
688 defer resp.Body.Close()
690 return resp.Headers, nil
693 // GetObjectMeta gets object metadata.
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.
698 // objectKey object key
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.
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)
711 defer resp.Body.Close()
713 return resp.Headers, nil
716 // SetObjectACL updates the object's ACL.
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.
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.
727 // objectKey the target object key (to set the ACL on)
728 // objectAcl object ACL. Valid options are PrivateACL, PublicReadACL, PublicReadWriteACL.
730 // error it's nil if no error, otherwise it's an error object.
732 func (bucket Bucket) SetObjectACL(objectKey string, objectACL ACLType, options ...Option) error {
733 options = append(options, ObjectACL(objectACL))
734 params, _ := GetRawParams(options)
736 resp, err := bucket.do("PUT", objectKey, params, options, nil, nil)
740 defer resp.Body.Close()
741 return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
744 // GetObjectACL gets object's ACL
746 // objectKey the object to get ACL from.
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.
751 func (bucket Bucket) GetObjectACL(objectKey string, options ...Option) (GetObjectACLResult, error) {
752 var out GetObjectACLResult
753 params, _ := GetRawParams(options)
755 resp, err := bucket.do("GET", objectKey, params, options, nil, nil)
759 defer resp.Body.Close()
761 err = xmlUnmarshal(resp.Body, &out)
765 // PutSymlink creates a symlink (to point to an existing object)
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.
773 // symObjectKey the symlink object's key.
774 // targetObjectKey the target object key to point to.
776 // error it's nil if no error, otherwise it's an error object.
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)
786 defer resp.Body.Close()
787 return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
790 // GetSymlink gets the symlink object with the specified key.
791 // If the symlink object does not exist, returns 404.
793 // objectKey the symlink object's key.
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.
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)
805 defer resp.Body.Close()
807 targetObjectKey := resp.Headers.Get(HTTPHeaderOssSymlinkTarget)
808 targetObjectKey, err = url.QueryUnescape(targetObjectKey)
810 return resp.Headers, err
812 resp.Headers.Set(HTTPHeaderOssSymlinkTarget, targetObjectKey)
813 return resp.Headers, err
816 // RestoreObject restores the object from the archive storage.
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.
824 // objectKey object key to restore.
826 // error it's nil if no error, otherwise it's an error object.
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)
835 defer resp.Body.Close()
836 return CheckRespCode(resp.StatusCode, []int{http.StatusOK, http.StatusAccepted})
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)
846 if restoreConfig.Days == 0 {
847 restoreConfig.Days = 1
850 bs, err := xml.Marshal(restoreConfig)
855 buffer := new(bytes.Buffer)
858 contentType := http.DetectContentType(buffer.Bytes())
859 options = append(options, ContentType(contentType))
861 params, _ := GetRawParams(options)
862 params["restore"] = nil
864 resp, err := bucket.do("POST", objectKey, params, options, buffer, nil)
868 defer resp.Body.Close()
869 return CheckRespCode(resp.StatusCode, []int{http.StatusOK, http.StatusAccepted})
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))
877 contentType := http.DetectContentType(buffer.Bytes())
878 options = append(options, ContentType(contentType))
880 params, _ := GetRawParams(options)
881 params["restore"] = nil
883 resp, err := bucket.do("POST", objectKey, params, options, buffer, nil)
887 defer resp.Body.Close()
888 return CheckRespCode(resp.StatusCode, []int{http.StatusOK, http.StatusAccepted})
891 // SignURL signs the URL. Users could access the object directly with this URL without getting the AK.
893 // objectKey the target object to sign.
894 // signURLConfig the configuration for the signed URL
896 // string returns the signed URL, when error is nil.
897 // error it's nil if no error, otherwise it's an error object.
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)
903 expiration := time.Now().Unix() + expiredInSec
905 params, err := GetRawParams(options)
910 headers := make(map[string]string)
911 err = handleOptions(headers, options)
916 return bucket.Client.Conn.signURL(method, bucket.BucketName, objectKey, expiration, params, headers), nil
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.
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
928 // error it's nil if no error, otherwise it's an error object.
930 func (bucket Bucket) PutObjectWithURL(signedURL string, reader io.Reader, options ...Option) error {
931 resp, err := bucket.DoPutObjectWithURL(signedURL, reader, options)
935 defer resp.Body.Close()
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.
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.
947 // error it's nil if no error, otherwise it's an error object.
949 func (bucket Bucket) PutObjectFromFileWithURL(signedURL, filePath string, options ...Option) error {
950 fd, err := os.Open(filePath)
956 resp, err := bucket.DoPutObjectWithURL(signedURL, fd, options)
960 defer resp.Body.Close()
965 // DoPutObjectWithURL is the actual API that does the upload with URL work(internal for SDK)
967 // signedURL the signed URL.
968 // reader io.Reader the read instance for getting the data to upload.
969 // options options for uploading.
971 // Response the response object which contains the HTTP response.
972 // error it's nil if no error, otherwise it's an error object.
974 func (bucket Bucket) DoPutObjectWithURL(signedURL string, reader io.Reader, options []Option) (*Response, error) {
975 listener := GetProgressListener(options)
977 params := map[string]interface{}{}
978 resp, err := bucket.doURL("PUT", signedURL, params, options, reader, listener)
983 if bucket.GetConfig().IsEnableCRC {
984 err = CheckCRC(resp, "DoPutObjectWithURL")
990 err = CheckRespCode(resp.StatusCode, []int{http.StatusOK})
995 // GetObjectWithURL downloads the object and returns the reader instance, with the signed URL.
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
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.
1005 func (bucket Bucket) GetObjectWithURL(signedURL string, options ...Option) (io.ReadCloser, error) {
1006 result, err := bucket.DoGetObjectWithURL(signedURL, options)
1010 return result.Response, nil
1013 // GetObjectToFileWithURL downloads the object into a local file with the signed URL.
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.
1019 // error it's nil if no error, otherwise it's an error object.
1021 func (bucket Bucket) GetObjectToFileWithURL(signedURL, filePath string, options ...Option) error {
1022 tempFilePath := filePath + TempFileSuffix
1024 // Get the object's content
1025 result, err := bucket.DoGetObjectWithURL(signedURL, options)
1029 defer result.Response.Close()
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)
1037 // Save the data to the file.
1038 _, err = io.Copy(fd, result.Response.Body)
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)
1052 if bucket.GetConfig().IsEnableCRC && !hasRange && acceptEncoding != "gzip" {
1053 result.Response.ClientCRC = result.ClientCRC.Sum64()
1054 err = CheckCRC(result.Response, "GetObjectToFileWithURL")
1056 os.Remove(tempFilePath)
1061 return os.Rename(tempFilePath, filePath)
1064 // DoGetObjectWithURL is the actual API that downloads the file with the signed URL.
1066 // signedURL the signed URL.
1067 // options the options for getting object. Check out parameter options in GetObject for the reference.
1069 // GetObjectResult the result object when the error is nil.
1070 // error it's nil if no error, otherwise it's an error object.
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)
1079 result := &GetObjectResult{
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
1093 listener := GetProgressListener(options)
1095 contentLen, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderContentLength), 10, 64)
1096 resp.Body = TeeReader(resp.Body, crcCalc, contentLen, listener, nil)
1102 // ProcessObject apply process on the specified image file.
1104 // The supported process includes resize, rotate, crop, watermark, format,
1105 // udf, customized style, etc.
1108 // objectKey object key to process.
1109 // process process string, such as "image/resize,w_100|sys/saveas,o_dGVzdC5qcGc,b_dGVzdA"
1111 // error it's nil if no error, otherwise it's an error object.
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)
1123 defer resp.Body.Close()
1125 err = jsonUnmarshal(resp.Body, &out)
1130 // PutObjectTagging add tagging to object
1132 // objectKey object key to add tagging
1133 // tagging tagging to be added
1135 // error nil if success, otherwise error
1137 func (bucket Bucket) PutObjectTagging(objectKey string, tagging Tagging, options ...Option) error {
1138 bs, err := xml.Marshal(tagging)
1143 buffer := new(bytes.Buffer)
1146 params, _ := GetRawParams(options)
1147 params["tagging"] = nil
1148 resp, err := bucket.do("PUT", objectKey, params, options, buffer, nil)
1152 defer resp.Body.Close()
1158 // GetObjectTagging get tagging of the object
1160 // objectKey object key to get tagging
1163 // error nil if success, otherwise error
1165 func (bucket Bucket) GetObjectTagging(objectKey string, options ...Option) (GetObjectTaggingResult, error) {
1166 var out GetObjectTaggingResult
1167 params, _ := GetRawParams(options)
1168 params["tagging"] = nil
1170 resp, err := bucket.do("GET", objectKey, params, options, nil, nil)
1174 defer resp.Body.Close()
1176 err = xmlUnmarshal(resp.Body, &out)
1181 // DeleteObjectTagging delete object taggging
1183 // objectKey object key to delete tagging
1185 // error nil if success, otherwise error
1187 func (bucket Bucket) DeleteObjectTagging(objectKey string, options ...Option) error {
1188 params, _ := GetRawParams(options)
1189 params["tagging"] = nil
1191 if objectKey == "" {
1192 return fmt.Errorf("invalid argument: object name is empty")
1195 resp, err := bucket.do("DELETE", objectKey, params, options, nil, nil)
1199 defer resp.Body.Close()
1201 return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
1204 func (bucket Bucket) OptionsMethod(objectKey string, options ...Option) (http.Header, error) {
1206 resp, err := bucket.do("OPTIONS", objectKey, nil, options, nil, nil)
1210 defer resp.Body.Close()
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)
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)
1230 err = CheckBucketName(bucket.BucketName)
1231 if len(bucket.BucketName) > 0 && err != nil {
1235 resp, err := bucket.Client.Conn.Do(method, bucket.BucketName, objectName,
1236 params, headers, data, 0, listener)
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
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)
1256 resp, err := bucket.Client.Conn.DoURL(method, signedURL, headers, data, 0, listener)
1258 // get response header
1259 respHeader, _ := FindOption(options, responseHeader, nil)
1260 if respHeader != nil {
1261 pRespHeader := respHeader.(*http.Header)
1262 *pRespHeader = resp.Headers
1268 func (bucket Bucket) GetConfig() *Config {
1269 return bucket.Client.Config
1272 func AddContentType(options []Option, keys ...string) []Option {
1273 typ := TypeByExtension("")
1274 for _, key := range keys {
1275 typ = TypeByExtension(key)
1282 typ = "application/octet-stream"
1285 opts := []Option{ContentType(typ)}
1286 opts = append(opts, options...)