github.com/aliyun/aliyun-oss-go-sdk@v3.0.2+incompatible/oss/bucket.go (about)

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