Kaynağa Gözat

merge client side crypto

taowei.wtw 5 yıl önce
ebeveyn
işleme
7008c00c6d
53 değiştirilmiş dosya ile 4704 ekleme ve 751 silme
  1. 75 69
      oss/bucket.go
  2. 38 37
      oss/bucket_credential_test.go
  3. 156 156
      oss/bucket_test.go
  4. 31 30
      oss/client.go
  5. 91 108
      oss/client_test.go
  6. 2 0
      oss/conf.go
  7. 4 14
      oss/conn.go
  8. 3 3
      oss/conn_test.go
  9. 1 1
      oss/const.go
  10. 4 4
      oss/crc_test.go
  11. 68 0
      oss/crypto/aes_ctr.go
  12. 153 0
      oss/crypto/aes_ctr_cipher.go
  13. 41 0
      oss/crypto/aes_ctr_cipher_test.go
  14. 69 0
      oss/crypto/cipher.go
  15. 32 0
      oss/crypto/cipher_test.go
  16. 539 0
      oss/crypto/crypto_bucket.go
  17. 1226 0
      oss/crypto/crypto_bucket_test.go
  18. 26 0
      oss/crypto/crypto_const.go
  19. 12 0
      oss/crypto/crypto_download.go
  20. 12 0
      oss/crypto/crypto_multicopy.go
  21. 174 0
      oss/crypto/crypto_multipart.go
  22. 769 0
      oss/crypto/crypto_multipart_test.go
  23. 128 0
      oss/crypto/crypto_type.go
  24. 12 0
      oss/crypto/crypto_upload.go
  25. 85 0
      oss/crypto/master_alikms_cipher.go
  26. 89 0
      oss/crypto/master_alikms_cipher_test.go
  27. 102 0
      oss/crypto/master_rsa_cipher.go
  28. 41 0
      oss/crypto/master_rsa_cipher_test.go
  29. 25 36
      oss/download.go
  30. 45 45
      oss/download_test.go
  31. 4 4
      oss/error.go
  32. 6 6
      oss/error_test.go
  33. 4 4
      oss/livechannel.go
  34. 4 4
      oss/livechannel_test.go
  35. 5 5
      oss/multicopy.go
  36. 19 19
      oss/multicopy_test.go
  37. 11 11
      oss/multipart.go
  38. 24 24
      oss/multipart_test.go
  39. 9 4
      oss/option.go
  40. 9 9
      oss/option_test.go
  41. 16 16
      oss/progress_test.go
  42. 3 3
      oss/select_object.go
  43. 4 4
      oss/type.go
  44. 2 2
      oss/type_test.go
  45. 8 9
      oss/upload.go
  46. 30 30
      oss/upload_test.go
  47. 146 51
      oss/utils.go
  48. 42 42
      oss/utils_test.go
  49. 1 1
      sample/append_object.go
  50. BIN
      sample/test-client-encryption-crypto-cpp-rsa.jpg
  51. BIN
      sample/test-client-encryption-crypto-python-rsa.jpg
  52. BIN
      sample/test-client-encryption-src.jpg
  53. 304 0
      sample_crypto/sample_crypto.go

+ 75 - 69
oss/bucket.go

@@ -34,7 +34,7 @@ type Bucket struct {
 // error    it's nil if no error, otherwise it's an error object.
 //
 func (bucket Bucket) PutObject(objectKey string, reader io.Reader, options ...Option) error {
-	opts := addContentType(options, objectKey)
+	opts := AddContentType(options, objectKey)
 
 	request := &PutObjectRequest{
 		ObjectKey: objectKey,
@@ -64,7 +64,7 @@ func (bucket Bucket) PutObjectFromFile(objectKey, filePath string, options ...Op
 	}
 	defer fd.Close()
 
-	opts := addContentType(options, filePath, objectKey)
+	opts := AddContentType(options, filePath, objectKey)
 
 	request := &PutObjectRequest{
 		ObjectKey: objectKey,
@@ -88,12 +88,12 @@ func (bucket Bucket) PutObjectFromFile(objectKey, filePath string, options ...Op
 // error    it's nil if no error, otherwise it's an error object.
 //
 func (bucket Bucket) DoPutObject(request *PutObjectRequest, options []Option) (*Response, error) {
-	isOptSet, _, _ := isOptionSet(options, HTTPHeaderContentType)
+	isOptSet, _, _ := IsOptionSet(options, HTTPHeaderContentType)
 	if !isOptSet {
-		options = addContentType(options, request.ObjectKey)
+		options = AddContentType(options, request.ObjectKey)
 	}
 
-	listener := getProgressListener(options)
+	listener := GetProgressListener(options)
 
 	params := map[string]interface{}{}
 	resp, err := bucket.do("PUT", request.ObjectKey, params, options, request.Reader, listener)
@@ -101,14 +101,14 @@ func (bucket Bucket) DoPutObject(request *PutObjectRequest, options []Option) (*
 		return nil, err
 	}
 
-	if bucket.getConfig().IsEnableCRC {
-		err = checkCRC(resp, "DoPutObject")
+	if bucket.GetConfig().IsEnableCRC {
+		err = CheckCRC(resp, "DoPutObject")
 		if err != nil {
 			return resp, err
 		}
 	}
 
-	err = checkRespCode(resp.StatusCode, []int{http.StatusOK})
+	err = CheckRespCode(resp.StatusCode, []int{http.StatusOK})
 
 	return resp, err
 }
@@ -164,15 +164,15 @@ func (bucket Bucket) GetObjectToFile(objectKey, filePath string, options ...Opti
 	}
 
 	// Compares the CRC value
-	hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
-	encodeOpt, _ := findOption(options, HTTPHeaderAcceptEncoding, nil)
+	hasRange, _, _ := IsOptionSet(options, HTTPHeaderRange)
+	encodeOpt, _ := FindOption(options, HTTPHeaderAcceptEncoding, nil)
 	acceptEncoding := ""
 	if encodeOpt != nil {
 		acceptEncoding = encodeOpt.(string)
 	}
-	if bucket.getConfig().IsEnableCRC && !hasRange && acceptEncoding != "gzip" {
+	if bucket.GetConfig().IsEnableCRC && !hasRange && acceptEncoding != "gzip" {
 		result.Response.ClientCRC = result.ClientCRC.Sum64()
-		err = checkCRC(result.Response, "GetObjectToFile")
+		err = CheckCRC(result.Response, "GetObjectToFile")
 		if err != nil {
 			os.Remove(tempFilePath)
 			return err
@@ -191,7 +191,7 @@ func (bucket Bucket) GetObjectToFile(objectKey, filePath string, options ...Opti
 // error    it's nil if no error, otherwise it's an error object.
 //
 func (bucket Bucket) DoGetObject(request *GetObjectRequest, options []Option) (*GetObjectResult, error) {
-	params, _ := getRawParams(options)
+	params, _ := GetRawParams(options)
 	resp, err := bucket.do("GET", request.ObjectKey, params, options, nil, nil)
 	if err != nil {
 		return nil, err
@@ -203,15 +203,15 @@ func (bucket Bucket) DoGetObject(request *GetObjectRequest, options []Option) (*
 
 	// CRC
 	var crcCalc hash.Hash64
-	hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
-	if bucket.getConfig().IsEnableCRC && !hasRange {
-		crcCalc = crc64.New(crcTable())
+	hasRange, _, _ := IsOptionSet(options, HTTPHeaderRange)
+	if bucket.GetConfig().IsEnableCRC && !hasRange {
+		crcCalc = crc64.New(CrcTable())
 		result.ServerCRC = resp.ServerCRC
 		result.ClientCRC = crcCalc
 	}
 
 	// Progress
-	listener := getProgressListener(options)
+	listener := GetProgressListener(options)
 
 	contentLen, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderContentLength), 10, 64)
 	resp.Body = TeeReader(resp.Body, crcCalc, contentLen, listener, nil)
@@ -236,11 +236,11 @@ func (bucket Bucket) CopyObject(srcObjectKey, destObjectKey string, options ...O
 
 	//first find version id
 	versionIdKey := "versionId"
-	versionId, _ := findOption(options, versionIdKey, nil)
+	versionId, _ := FindOption(options, versionIdKey, nil)
 	if versionId == nil {
 		options = append(options, CopySource(bucket.BucketName, url.QueryEscape(srcObjectKey)))
 	} else {
-		options = deleteOption(options, versionIdKey)
+		options = DeleteOption(options, versionIdKey)
 		options = append(options, CopySourceVersion(bucket.BucketName, url.QueryEscape(srcObjectKey), versionId.(string)))
 	}
 
@@ -294,11 +294,11 @@ func (bucket Bucket) copy(srcObjectKey, destBucketName, destObjectKey string, op
 
 	//first find version id
 	versionIdKey := "versionId"
-	versionId, _ := findOption(options, versionIdKey, nil)
+	versionId, _ := FindOption(options, versionIdKey, nil)
 	if versionId == nil {
 		options = append(options, CopySource(bucket.BucketName, url.QueryEscape(srcObjectKey)))
 	} else {
-		options = deleteOption(options, versionIdKey)
+		options = DeleteOption(options, versionIdKey)
 		options = append(options, CopySourceVersion(bucket.BucketName, url.QueryEscape(srcObjectKey), versionId.(string)))
 	}
 
@@ -311,7 +311,7 @@ func (bucket Bucket) copy(srcObjectKey, destBucketName, destObjectKey string, op
 	resp, err := bucket.Client.Conn.Do("PUT", destBucketName, destObjectKey, params, headers, nil, 0, nil)
 
 	// get response header
-	respHeader, _ := findOption(options, responseHeader, nil)
+	respHeader, _ := FindOption(options, responseHeader, nil)
 	if respHeader != nil {
 		pRespHeader := respHeader.(*http.Header)
 		*pRespHeader = resp.Headers
@@ -371,23 +371,23 @@ func (bucket Bucket) DoAppendObject(request *AppendObjectRequest, options []Opti
 	params["position"] = strconv.FormatInt(request.Position, 10)
 	headers := make(map[string]string)
 
-	opts := addContentType(options, request.ObjectKey)
+	opts := AddContentType(options, request.ObjectKey)
 	handleOptions(headers, opts)
 
 	var initCRC uint64
-	isCRCSet, initCRCOpt, _ := isOptionSet(options, initCRC64)
+	isCRCSet, initCRCOpt, _ := IsOptionSet(options, initCRC64)
 	if isCRCSet {
 		initCRC = initCRCOpt.(uint64)
 	}
 
-	listener := getProgressListener(options)
+	listener := GetProgressListener(options)
 
 	handleOptions(headers, opts)
 	resp, err := bucket.Client.Conn.Do("POST", bucket.BucketName, request.ObjectKey, params, headers,
 		request.Reader, initCRC, listener)
 
 	// get response header
-	respHeader, _ := findOption(options, responseHeader, nil)
+	respHeader, _ := FindOption(options, responseHeader, nil)
 	if respHeader != nil {
 		pRespHeader := respHeader.(*http.Header)
 		*pRespHeader = resp.Headers
@@ -404,8 +404,8 @@ func (bucket Bucket) DoAppendObject(request *AppendObjectRequest, options []Opti
 		CRC:          resp.ServerCRC,
 	}
 
-	if bucket.getConfig().IsEnableCRC && isCRCSet {
-		err = checkCRC(resp, "AppendObject")
+	if bucket.GetConfig().IsEnableCRC && isCRCSet {
+		err = CheckCRC(resp, "AppendObject")
 		if err != nil {
 			return result, err
 		}
@@ -421,13 +421,13 @@ func (bucket Bucket) DoAppendObject(request *AppendObjectRequest, options []Opti
 // error    it's nil if no error, otherwise it's an error object.
 //
 func (bucket Bucket) DeleteObject(objectKey string, options ...Option) error {
-	params, _ := getRawParams(options)
+	params, _ := GetRawParams(options)
 	resp, err := bucket.do("DELETE", objectKey, params, options, nil, nil)
 	if err != nil {
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
 }
 
 // DeleteObjects deletes multiple objects.
@@ -446,7 +446,7 @@ func (bucket Bucket) DeleteObjects(objectKeys []string, options ...Option) (Dele
 		dxml.Objects = append(dxml.Objects, DeleteObject{Key: key})
 	}
 
-	isQuiet, _ := findOption(options, deleteObjectsQuiet, false)
+	isQuiet, _ := FindOption(options, deleteObjectsQuiet, false)
 	dxml.Quiet = isQuiet.(bool)
 
 	bs, err := xml.Marshal(dxml)
@@ -502,7 +502,7 @@ func (bucket Bucket) DeleteObjectVersions(objectVersions []DeleteObject, options
 	dxml := deleteXML{}
 	dxml.Objects = objectVersions
 
-	isQuiet, _ := findOption(options, deleteObjectsQuiet, false)
+	isQuiet, _ := FindOption(options, deleteObjectsQuiet, false)
 	dxml.Quiet = isQuiet.(bool)
 
 	bs, err := xml.Marshal(dxml)
@@ -582,7 +582,7 @@ func (bucket Bucket) ListObjects(options ...Option) (ListObjectsResult, error) {
 	var out ListObjectsResult
 
 	options = append(options, EncodingType("url"))
-	params, err := getRawParams(options)
+	params, err := GetRawParams(options)
 	if err != nil {
 		return out, err
 	}
@@ -607,7 +607,7 @@ func (bucket Bucket) ListObjectVersions(options ...Option) (ListObjectVersionsRe
 	var out ListObjectVersionsResult
 
 	options = append(options, EncodingType("url"))
-	params, err := getRawParams(options)
+	params, err := GetRawParams(options)
 	if err != nil {
 		return out, err
 	}
@@ -652,7 +652,7 @@ func (bucket Bucket) SetObjectMeta(objectKey string, options ...Option) error {
 // error    it's nil if no error, otherwise it's an error object.
 //
 func (bucket Bucket) GetObjectDetailedMeta(objectKey string, options ...Option) (http.Header, error) {
-	params, _ := getRawParams(options)
+	params, _ := GetRawParams(options)
 	resp, err := bucket.do("HEAD", objectKey, params, options, nil, nil)
 	if err != nil {
 		return nil, err
@@ -673,7 +673,7 @@ func (bucket Bucket) GetObjectDetailedMeta(objectKey string, options ...Option)
 // error    it's nil if no error, otherwise it's an error object.
 //
 func (bucket Bucket) GetObjectMeta(objectKey string, options ...Option) (http.Header, error) {
-	params, _ := getRawParams(options)
+	params, _ := GetRawParams(options)
 	params["objectMeta"] = nil
 	//resp, err := bucket.do("GET", objectKey, "?objectMeta", "", nil, nil, nil)
 	resp, err := bucket.do("HEAD", objectKey, params, options, nil, nil)
@@ -703,14 +703,14 @@ func (bucket Bucket) GetObjectMeta(objectKey string, options ...Option) (http.He
 //
 func (bucket Bucket) SetObjectACL(objectKey string, objectACL ACLType, options ...Option) error {
 	options = append(options, ObjectACL(objectACL))
-	params, _ := getRawParams(options)
+	params, _ := GetRawParams(options)
 	params["acl"] = nil
 	resp, err := bucket.do("PUT", objectKey, params, options, nil, nil)
 	if err != nil {
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 // GetObjectACL gets object's ACL
@@ -722,7 +722,7 @@ func (bucket Bucket) SetObjectACL(objectKey string, objectACL ACLType, options .
 //
 func (bucket Bucket) GetObjectACL(objectKey string, options ...Option) (GetObjectACLResult, error) {
 	var out GetObjectACLResult
-	params, _ := getRawParams(options)
+	params, _ := GetRawParams(options)
 	params["acl"] = nil
 	resp, err := bucket.do("GET", objectKey, params, options, nil, nil)
 	if err != nil {
@@ -749,14 +749,14 @@ func (bucket Bucket) GetObjectACL(objectKey string, options ...Option) (GetObjec
 //
 func (bucket Bucket) PutSymlink(symObjectKey string, targetObjectKey string, options ...Option) error {
 	options = append(options, symlinkTarget(url.QueryEscape(targetObjectKey)))
-	params, _ := getRawParams(options)
+	params, _ := GetRawParams(options)
 	params["symlink"] = nil
 	resp, err := bucket.do("PUT", symObjectKey, params, options, nil, nil)
 	if err != nil {
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 // GetSymlink gets the symlink object with the specified key.
@@ -768,7 +768,7 @@ func (bucket Bucket) PutSymlink(symObjectKey string, targetObjectKey string, opt
 //          When error is nil, the target file key is in the X-Oss-Symlink-Target header of the returned object.
 //
 func (bucket Bucket) GetSymlink(objectKey string, options ...Option) (http.Header, error) {
-	params, _ := getRawParams(options)
+	params, _ := GetRawParams(options)
 	params["symlink"] = nil
 	resp, err := bucket.do("GET", objectKey, params, options, nil, nil)
 	if err != nil {
@@ -798,14 +798,14 @@ func (bucket Bucket) GetSymlink(objectKey string, options ...Option) (http.Heade
 // error    it's nil if no error, otherwise it's an error object.
 //
 func (bucket Bucket) RestoreObject(objectKey string, options ...Option) error {
-	params, _ := getRawParams(options)
+	params, _ := GetRawParams(options)
 	params["restore"] = nil
 	resp, err := bucket.do("POST", objectKey, params, options, nil, nil)
 	if err != nil {
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusOK, http.StatusAccepted})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusOK, http.StatusAccepted})
 }
 
 // RestoreObjectDetail support more features than RestoreObject
@@ -830,7 +830,7 @@ func (bucket Bucket) RestoreObjectDetail(objectKey string, restoreConfig Restore
 	contentType := http.DetectContentType(buffer.Bytes())
 	options = append(options, ContentType(contentType))
 
-	params, _ := getRawParams(options)
+	params, _ := GetRawParams(options)
 	params["restore"] = nil
 
 	resp, err := bucket.do("POST", objectKey, params, options, buffer, nil)
@@ -838,7 +838,7 @@ func (bucket Bucket) RestoreObjectDetail(objectKey string, restoreConfig Restore
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusOK, http.StatusAccepted})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusOK, http.StatusAccepted})
 }
 
 // SignURL signs the URL. Users could access the object directly with this URL without getting the AK.
@@ -855,7 +855,7 @@ func (bucket Bucket) SignURL(objectKey string, method HTTPMethod, expiredInSec i
 	}
 	expiration := time.Now().Unix() + expiredInSec
 
-	params, err := getRawParams(options)
+	params, err := GetRawParams(options)
 	if err != nil {
 		return "", err
 	}
@@ -925,7 +925,7 @@ func (bucket Bucket) PutObjectFromFileWithURL(signedURL, filePath string, option
 // error    it's nil if no error, otherwise it's an error object.
 //
 func (bucket Bucket) DoPutObjectWithURL(signedURL string, reader io.Reader, options []Option) (*Response, error) {
-	listener := getProgressListener(options)
+	listener := GetProgressListener(options)
 
 	params := map[string]interface{}{}
 	resp, err := bucket.doURL("PUT", signedURL, params, options, reader, listener)
@@ -933,14 +933,14 @@ func (bucket Bucket) DoPutObjectWithURL(signedURL string, reader io.Reader, opti
 		return nil, err
 	}
 
-	if bucket.getConfig().IsEnableCRC {
-		err = checkCRC(resp, "DoPutObjectWithURL")
+	if bucket.GetConfig().IsEnableCRC {
+		err = CheckCRC(resp, "DoPutObjectWithURL")
 		if err != nil {
 			return resp, err
 		}
 	}
 
-	err = checkRespCode(resp.StatusCode, []int{http.StatusOK})
+	err = CheckRespCode(resp.StatusCode, []int{http.StatusOK})
 
 	return resp, err
 }
@@ -995,16 +995,16 @@ func (bucket Bucket) GetObjectToFileWithURL(signedURL, filePath string, options
 	}
 
 	// Compare the CRC value. If CRC values do not match, return error.
-	hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
-	encodeOpt, _ := findOption(options, HTTPHeaderAcceptEncoding, nil)
+	hasRange, _, _ := IsOptionSet(options, HTTPHeaderRange)
+	encodeOpt, _ := FindOption(options, HTTPHeaderAcceptEncoding, nil)
 	acceptEncoding := ""
 	if encodeOpt != nil {
 		acceptEncoding = encodeOpt.(string)
 	}
 
-	if bucket.getConfig().IsEnableCRC && !hasRange && acceptEncoding != "gzip" {
+	if bucket.GetConfig().IsEnableCRC && !hasRange && acceptEncoding != "gzip" {
 		result.Response.ClientCRC = result.ClientCRC.Sum64()
-		err = checkCRC(result.Response, "GetObjectToFileWithURL")
+		err = CheckCRC(result.Response, "GetObjectToFileWithURL")
 		if err != nil {
 			os.Remove(tempFilePath)
 			return err
@@ -1023,7 +1023,7 @@ func (bucket Bucket) GetObjectToFileWithURL(signedURL, filePath string, options
 // error    it's nil if no error, otherwise it's an error object.
 //
 func (bucket Bucket) DoGetObjectWithURL(signedURL string, options []Option) (*GetObjectResult, error) {
-	params, _ := getRawParams(options)
+	params, _ := GetRawParams(options)
 	resp, err := bucket.doURL("GET", signedURL, params, options, nil, nil)
 	if err != nil {
 		return nil, err
@@ -1035,15 +1035,15 @@ func (bucket Bucket) DoGetObjectWithURL(signedURL string, options []Option) (*Ge
 
 	// CRC
 	var crcCalc hash.Hash64
-	hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
-	if bucket.getConfig().IsEnableCRC && !hasRange {
-		crcCalc = crc64.New(crcTable())
+	hasRange, _, _ := IsOptionSet(options, HTTPHeaderRange)
+	if bucket.GetConfig().IsEnableCRC && !hasRange {
+		crcCalc = crc64.New(CrcTable())
 		result.ServerCRC = resp.ServerCRC
 		result.ClientCRC = crcCalc
 	}
 
 	// Progress
-	listener := getProgressListener(options)
+	listener := GetProgressListener(options)
 
 	contentLen, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderContentLength), 10, 64)
 	resp.Body = TeeReader(resp.Body, crcCalc, contentLen, listener, nil)
@@ -1065,7 +1065,7 @@ func (bucket Bucket) DoGetObjectWithURL(signedURL string, options []Option) (*Ge
 //
 func (bucket Bucket) ProcessObject(objectKey string, process string, options ...Option) (ProcessObjectResult, error) {
 	var out ProcessObjectResult
-	params, _ := getRawParams(options)
+	params, _ := GetRawParams(options)
 	params["x-oss-process"] = nil
 	processData := fmt.Sprintf("%v=%v", "x-oss-process", process)
 	data := strings.NewReader(processData)
@@ -1096,7 +1096,7 @@ func (bucket Bucket) PutObjectTagging(objectKey string, tagging Tagging, options
 	buffer := new(bytes.Buffer)
 	buffer.Write(bs)
 
-	params, _ := getRawParams(options)
+	params, _ := GetRawParams(options)
 	params["tagging"] = nil
 	resp, err := bucket.do("PUT", objectKey, params, options, buffer, nil)
 	if err != nil {
@@ -1117,7 +1117,7 @@ func (bucket Bucket) PutObjectTagging(objectKey string, tagging Tagging, options
 
 func (bucket Bucket) GetObjectTagging(objectKey string, options ...Option) (GetObjectTaggingResult, error) {
 	var out GetObjectTaggingResult
-	params, _ := getRawParams(options)
+	params, _ := GetRawParams(options)
 	params["tagging"] = nil
 
 	resp, err := bucket.do("GET", objectKey, params, options, nil, nil)
@@ -1138,7 +1138,7 @@ func (bucket Bucket) GetObjectTagging(objectKey string, options ...Option) (GetO
 // error      nil if success, otherwise error
 //
 func (bucket Bucket) DeleteObjectTagging(objectKey string, options ...Option) error {
-	params, _ := getRawParams(options)
+	params, _ := GetRawParams(options)
 	params["tagging"] = nil
 
 	if objectKey == "" {
@@ -1151,7 +1151,7 @@ func (bucket Bucket) DeleteObjectTagging(objectKey string, options ...Option) er
 	}
 	defer resp.Body.Close()
 
-	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
 }
 
 func (bucket Bucket) OptionsMethod(objectKey string, options ...Option) (http.Header, error) {
@@ -1165,6 +1165,12 @@ func (bucket Bucket) OptionsMethod(objectKey string, options ...Option) (http.He
 	return out, nil
 }
 
+// public
+func (bucket Bucket) Do(method, objectName string, params map[string]interface{}, options []Option,
+	data io.Reader, listener ProgressListener) (*Response, error) {
+	return bucket.do(method, objectName, params, options, data, listener)
+}
+
 // Private
 func (bucket Bucket) do(method, objectName string, params map[string]interface{}, options []Option,
 	data io.Reader, listener ProgressListener) (*Response, error) {
@@ -1183,7 +1189,7 @@ func (bucket Bucket) do(method, objectName string, params map[string]interface{}
 		params, headers, data, 0, listener)
 
 	// get response header
-	respHeader, _ := findOption(options, responseHeader, nil)
+	respHeader, _ := FindOption(options, responseHeader, nil)
 	if respHeader != nil {
 		pRespHeader := respHeader.(*http.Header)
 		*pRespHeader = resp.Headers
@@ -1203,7 +1209,7 @@ func (bucket Bucket) doURL(method HTTPMethod, signedURL string, params map[strin
 	resp, err := bucket.Client.Conn.DoURL(method, signedURL, headers, data, 0, listener)
 
 	// get response header
-	respHeader, _ := findOption(options, responseHeader, nil)
+	respHeader, _ := FindOption(options, responseHeader, nil)
 	if respHeader != nil {
 		pRespHeader := respHeader.(*http.Header)
 		*pRespHeader = resp.Headers
@@ -1212,11 +1218,11 @@ func (bucket Bucket) doURL(method HTTPMethod, signedURL string, params map[strin
 	return resp, err
 }
 
-func (bucket Bucket) getConfig() *Config {
+func (bucket Bucket) GetConfig() *Config {
 	return bucket.Client.Config
 }
 
-func addContentType(options []Option, keys ...string) []Option {
+func AddContentType(options []Option, keys ...string) []Option {
 	typ := TypeByExtension("")
 	for _, key := range keys {
 		typ = TypeByExtension(key)

+ 38 - 37
oss/bucket_credential_test.go

@@ -2,25 +2,26 @@
 package oss
 
 import (
-	"os"
-	"strings"
+	"bytes"
 	"io/ioutil"
 	"math/rand"
-	"bytes"
+	"os"
 	"strconv"
+	"strings"
+
 	. "gopkg.in/check.v1"
 )
 
 type OssCredentialBucketSuite struct {
-	client        *Client
-	creClient     *Client
-	bucket        *Bucket
-	creBucket     *Bucket
+	client    *Client
+	creClient *Client
+	bucket    *Bucket
+	creBucket *Bucket
 }
 
 var _ = Suite(&OssCredentialBucketSuite{})
 
-func (cs *OssCredentialBucketSuite)credentialSubUser(c *C) {
+func (cs *OssCredentialBucketSuite) credentialSubUser(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 	err = client.CreateBucket(credentialBucketName)
@@ -35,7 +36,7 @@ func (cs *OssCredentialBucketSuite)credentialSubUser(c *C) {
 					"oss:*"
 				],
 				"Effect":"Allow",
-				"Principal":["`+ credentialUID + `"],
+				"Principal":["` + credentialUID + `"],
 				"Resource":["acs:oss:*:*:` + credentialBucketName + `", "acs:oss:*:*:` + credentialBucketName + `/*"]
 			}
 		]
@@ -51,7 +52,7 @@ func (cs *OssCredentialBucketSuite)credentialSubUser(c *C) {
 
 // SetUpSuite runs once when the suite starts running.
 func (cs *OssCredentialBucketSuite) SetUpSuite(c *C) {
-	if credentialUID == ""{
+	if credentialUID == "" {
 		testLogger.Println("the cerdential UID is NULL, skip the credential test")
 		c.Skip("the credential Uid is null")
 	}
@@ -69,7 +70,7 @@ func (cs *OssCredentialBucketSuite) SetUpSuite(c *C) {
 }
 
 func (cs *OssCredentialBucketSuite) TearDownSuite(c *C) {
-	if credentialUID == ""{
+	if credentialUID == "" {
 		c.Skip("the credential Uid is null")
 	}
 	for _, bucket := range []*Bucket{cs.bucket} {
@@ -100,7 +101,7 @@ func (cs *OssCredentialBucketSuite) TearDownSuite(c *C) {
 				c.Assert(err, IsNil)
 			}
 			marker = Marker(lor.NextMarker)
-			if !lor.IsTruncated{
+			if !lor.IsTruncated {
 				break
 			}
 		}
@@ -114,13 +115,13 @@ func (cs *OssCredentialBucketSuite) TearDownSuite(c *C) {
 func (cs *OssCredentialBucketSuite) TestReqerPaymentNoRequester(c *C) {
 	// Set bucket is requester who send the request
 	reqPayConf := RequestPaymentConfiguration{
-		Payer:string(Requester),
+		Payer: string(Requester),
 	}
 	err := cs.client.SetBucketRequestPayment(credentialBucketName, reqPayConf)
 	c.Assert(err, IsNil)
 
-	key := objectNamePrefix + randStr(8)
-	objectValue := randStr(18)
+	key := objectNamePrefix + RandStr(8)
+	objectValue := RandStr(18)
 
 	// Put object
 	err = cs.creBucket.PutObject(key, strings.NewReader(objectValue))
@@ -147,13 +148,13 @@ func (cs *OssCredentialBucketSuite) TestReqerPaymentNoRequester(c *C) {
 func (cs *OssCredentialBucketSuite) TestReqerPaymentWithRequester(c *C) {
 	// Set bucket is requester who send the request
 	reqPayConf := RequestPaymentConfiguration{
-		Payer:string(Requester),
+		Payer: string(Requester),
 	}
 	err := cs.client.SetBucketRequestPayment(credentialBucketName, reqPayConf)
 	c.Assert(err, IsNil)
 
-	key := objectNamePrefix + randStr(8)
-	objectValue := randStr(18)
+	key := objectNamePrefix + RandStr(8)
+	objectValue := RandStr(18)
 
 	// Put object with a bucketowner
 	err = cs.creBucket.PutObject(key, strings.NewReader(objectValue), RequestPayer(BucketOwner))
@@ -190,13 +191,13 @@ func (cs *OssCredentialBucketSuite) TestReqerPaymentWithRequester(c *C) {
 func (cs *OssCredentialBucketSuite) TestOwnerPaymentNoRequester(c *C) {
 	// Set bucket is requester who send the request
 	reqPayConf := RequestPaymentConfiguration{
-		Payer:string(BucketOwner),
+		Payer: string(BucketOwner),
 	}
 	err := cs.client.SetBucketRequestPayment(credentialBucketName, reqPayConf)
 	c.Assert(err, IsNil)
 
-	key := objectNamePrefix + randStr(8)
-	objectValue := randStr(18)
+	key := objectNamePrefix + RandStr(8)
+	objectValue := RandStr(18)
 
 	// Put object
 	err = cs.creBucket.PutObject(key, strings.NewReader(objectValue))
@@ -224,14 +225,14 @@ func (cs *OssCredentialBucketSuite) TestOwnerPaymentNoRequester(c *C) {
 func (cs *OssCredentialBucketSuite) TestOwnerPaymentWithRequester(c *C) {
 	// Set bucket is BucketOwner payer
 	reqPayConf := RequestPaymentConfiguration{
-		Payer:string(BucketOwner),
+		Payer: string(BucketOwner),
 	}
 
 	err := cs.client.SetBucketRequestPayment(credentialBucketName, reqPayConf)
 	c.Assert(err, IsNil)
 
-	key := objectNamePrefix + randStr(8)
-	objectValue := randStr(18)
+	key := objectNamePrefix + RandStr(8)
+	objectValue := RandStr(18)
 
 	// Put object
 	err = cs.creBucket.PutObject(key, strings.NewReader(objectValue), RequestPayer(BucketOwner))
@@ -261,9 +262,9 @@ func (cs *OssCredentialBucketSuite) TestOwnerPaymentWithRequester(c *C) {
 
 // TestPutObjectFromFile
 func (cs *OssCredentialBucketSuite) TestPutObjectFromFile(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	localFile := "../sample/BingWallpaper-2015-11-07.jpg"
-	newFile := randStr(8) + ".jpg"
+	newFile := RandStr(8) + ".jpg"
 
 	// Put
 	err := cs.creBucket.PutObjectFromFile(objectName, localFile, RequestPayer(Requester))
@@ -322,8 +323,8 @@ func (cs *OssCredentialBucketSuite) TestPutObjectFromFile(c *C) {
 
 // TestCopyObject
 func (cs *OssCredentialBucketSuite) TestCopyObject(c *C) {
-	objectName := objectNamePrefix + randStr(8)
-	objectValue := randStr(18)
+	objectName := objectNamePrefix + RandStr(8)
+	objectValue := RandStr(18)
 
 	err := cs.creBucket.PutObject(objectName, strings.NewReader(objectValue),
 		ACL(ACLPublicRead), Meta("my", "myprop"), RequestPayer(Requester))
@@ -452,8 +453,8 @@ func (cs *OssCredentialBucketSuite) TestCopyObject(c *C) {
 
 // TestCopyObjectToOrFrom
 func (cs *OssCredentialBucketSuite) TestCopyObjectToOrFrom(c *C) {
-	objectName := objectNamePrefix + randStr(8)
-	objectValue := randStr(18)
+	objectName := objectNamePrefix + RandStr(8)
+	objectValue := RandStr(18)
 	sorBucketName := credentialBucketName + "-sor"
 	objectNameDest := objectName + "-Dest"
 
@@ -507,18 +508,18 @@ func (cs *OssCredentialBucketSuite) TestCopyObjectToOrFrom(c *C) {
 
 // TestAppendObject
 func (cs *OssCredentialBucketSuite) TestAppendObject(c *C) {
-	objectName := objectNamePrefix + randStr(8)
-	objectValue1 := randStr(18)
-	objectValue2 := randStr(18)
+	objectName := objectNamePrefix + RandStr(8)
+	objectValue1 := RandStr(18)
+	objectValue2 := RandStr(18)
 	objectValue := objectValue1 + objectValue2
 	var val = []byte(objectValue)
-	var localFile = randStr(8) + ".txt"
+	var localFile = RandStr(8) + ".txt"
 	var nextPos int64
 	var midPos = 1 + rand.Intn(len(val)-1)
 
-	var err = createFileAndWrite(localFile+"1", val[0:midPos])
+	var err = CreateFileAndWrite(localFile+"1", val[0:midPos])
 	c.Assert(err, IsNil)
-	err = createFileAndWrite(localFile+"2", val[midPos:])
+	err = CreateFileAndWrite(localFile+"2", val[midPos:])
 	c.Assert(err, IsNil)
 
 	// String append
@@ -612,4 +613,4 @@ func (cs *OssCredentialBucketSuite) TestAppendObject(c *C) {
 
 	err = cs.creBucket.DeleteObject(objectName, RequestPayer(Requester))
 	c.Assert(err, IsNil)
-}
+}

Dosya farkı çok büyük olduğundan ihmal edildi
+ 156 - 156
oss/bucket_test.go


+ 31 - 30
oss/client.go

@@ -115,8 +115,8 @@ func (client Client) CreateBucket(bucketName string, options ...Option) error {
 	var cbConfig createBucketConfiguration
 	cbConfig.StorageClass = StorageStandard
 
-	isStorageSet, valStroage, _ := isOptionSet(options, storageClass)
-	isRedundancySet, valRedundancy, _ := isOptionSet(options, redundancyType)
+	isStorageSet, valStroage, _ := IsOptionSet(options, storageClass)
+	isRedundancySet, valRedundancy, _ := IsOptionSet(options, redundancyType)
 	if isStorageSet {
 		cbConfig.StorageClass = valStroage.(StorageClassType)
 	}
@@ -140,7 +140,7 @@ func (client Client) CreateBucket(bucketName string, options ...Option) error {
 	}
 
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 // ListBuckets lists buckets of the current account under the given endpoint, with optional filters.
@@ -156,7 +156,7 @@ func (client Client) CreateBucket(bucketName string, options ...Option) error {
 func (client Client) ListBuckets(options ...Option) (ListBucketsResult, error) {
 	var out ListBucketsResult
 
-	params, err := getRawParams(options)
+	params, err := GetRawParams(options)
 	if err != nil {
 		return out, err
 	}
@@ -204,7 +204,7 @@ func (client Client) DeleteBucket(bucketName string, options ...Option) error {
 	}
 
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
 }
 
 // GetBucketLocation gets the bucket location.
@@ -247,7 +247,7 @@ func (client Client) SetBucketACL(bucketName string, bucketACL ACLType) error {
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 // GetBucketACL gets the bucket ACL.
@@ -306,7 +306,7 @@ func (client Client) SetBucketLifecycle(bucketName string, rules []LifecycleRule
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 // DeleteBucketLifecycle deletes the bucket's lifecycle.
@@ -324,7 +324,7 @@ func (client Client) DeleteBucketLifecycle(bucketName string) error {
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
 }
 
 // GetBucketLifecycle gets the bucket's lifecycle settings.
@@ -392,7 +392,7 @@ func (client Client) SetBucketReferer(bucketName string, referers []string, allo
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 // GetBucketReferer gets the bucket's referrer white list.
@@ -460,7 +460,7 @@ func (client Client) SetBucketLogging(bucketName, targetBucket, targetPrefix str
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 // DeleteBucketLogging deletes the logging configuration to disable the logging on the bucket.
@@ -477,7 +477,7 @@ func (client Client) DeleteBucketLogging(bucketName string) error {
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
 }
 
 // GetBucketLogging gets the bucket's logging settings
@@ -535,7 +535,7 @@ func (client Client) SetBucketWebsite(bucketName, indexDocument, errorDocument s
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 // SetBucketWebsiteDetail sets the bucket's static website's detail
@@ -568,7 +568,7 @@ func (client Client) SetBucketWebsiteDetail(bucketName string, wxml WebsiteXML,
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 // SetBucketWebsiteXml sets the bucket's static website's rule
@@ -597,7 +597,7 @@ func (client Client) SetBucketWebsiteXml(bucketName string, webXml string, optio
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 // DeleteBucketWebsite deletes the bucket's static web site settings.
@@ -614,7 +614,7 @@ func (client Client) DeleteBucketWebsite(bucketName string) error {
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
 }
 
 // GetBucketWebsite gets the bucket's default page (index page) and the error page.
@@ -677,7 +677,7 @@ func (client Client) SetBucketCORS(bucketName string, corsRules []CORSRule) erro
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 // DeleteBucketCORS deletes the bucket's static website settings.
@@ -694,7 +694,7 @@ func (client Client) DeleteBucketCORS(bucketName string) error {
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
 }
 
 // GetBucketCORS gets the bucket's CORS settings.
@@ -777,7 +777,7 @@ func (client Client) SetBucketVersioning(bucketName string, versioningConfig Ver
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 // GetBucketVersioning get bucket versioning status:Enabled、Suspended
@@ -825,7 +825,7 @@ func (client Client) SetBucketEncryption(bucketName string, encryptionRule Serve
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 // GetBucketEncryption get bucket encryption
@@ -858,7 +858,7 @@ func (client Client) DeleteBucketEncryption(bucketName string, options ...Option
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
 }
 
 //
@@ -889,7 +889,7 @@ func (client Client) SetBucketTagging(bucketName string, tagging Tagging, option
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 // GetBucketTagging get tagging of the bucket
@@ -922,7 +922,7 @@ func (client Client) DeleteBucketTagging(bucketName string, options ...Option) e
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
 }
 
 // GetBucketStat get bucket stat
@@ -990,7 +990,7 @@ func (client Client) SetBucketPolicy(bucketName string, policy string, options .
 	}
 	defer resp.Body.Close()
 
-	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 // DeleteBucketPolicy API operation for Object Storage Service.
@@ -1010,7 +1010,7 @@ func (client Client) DeleteBucketPolicy(bucketName string, options ...Option) er
 	}
 
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
 }
 
 // SetBucketRequestPayment API operation for Object Storage Service.
@@ -1046,7 +1046,7 @@ func (client Client) SetBucketRequestPayment(bucketName string, paymentConfig Re
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 // GetBucketRequestPayment API operation for Object Storage Service.
@@ -1129,7 +1129,7 @@ func (client Client) SetBucketQoSInfo(bucketName string, qosConf BucketQoSConfig
 	}
 
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 // GetBucketQosInfo API operation for Object Storage Service.
@@ -1175,7 +1175,7 @@ func (client Client) DeleteBucketQosInfo(bucketName string, options ...Option) e
 	}
 	defer resp.Body.Close()
 
-	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
 }
 
 // LimitUploadSpeed set upload bandwidth limit speed,default is 0,unlimited
@@ -1256,7 +1256,7 @@ func (client Client) SetBucketInventory(bucketName string, inventoryConfig Inven
 
 	defer resp.Body.Close()
 
-	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 // GetBucketInventory API operation for Object Storage Service
@@ -1340,7 +1340,7 @@ func (client Client) DeleteBucketInventory(bucketName, strInventoryId string, op
 	}
 	defer resp.Body.Close()
 
-	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
 }
 
 // SecurityToken sets the temporary user's SecurityToken.
@@ -1390,6 +1390,7 @@ func EnableCRC(isEnableCRC bool) ClientOption {
 func UserAgent(userAgent string) ClientOption {
 	return func(client *Client) {
 		client.Config.UserAgent = userAgent
+		client.Config.UserSetUa = true
 	}
 }
 
@@ -1488,7 +1489,7 @@ func (client Client) do(method, bucketName string, params map[string]interface{}
 	resp, err := client.Conn.Do(method, bucketName, "", params, headers, data, 0, nil)
 
 	// get response header
-	respHeader, _ := findOption(options, responseHeader, nil)
+	respHeader, _ := FindOption(options, responseHeader, nil)
 	if respHeader != nil {
 		pRespHeader := respHeader.(*http.Header)
 		*pRespHeader = resp.Headers

+ 91 - 108
oss/client_test.go

@@ -59,14 +59,14 @@ var (
 	// prefix of bucket name for bucket ops test
 	bucketNamePrefix = "go-sdk-test-bucket-"
 	// bucket name for object ops test
-	bucketName        = bucketNamePrefix + randLowStr(6)
-	archiveBucketName = bucketNamePrefix + "arch-" + randLowStr(6)
+	bucketName        = bucketNamePrefix + RandLowStr(6)
+	archiveBucketName = bucketNamePrefix + "arch-" + RandLowStr(6)
 	// object name for object ops test
 	objectNamePrefix = "go-sdk-test-object-"
 	// sts region is one and only hangzhou
 	stsRegion = "cn-hangzhou"
 	// Credentials
-	credentialBucketName = bucketNamePrefix + randLowStr(6)
+	credentialBucketName = bucketNamePrefix + RandLowStr(6)
 )
 
 var (
@@ -77,7 +77,7 @@ var (
 	timeoutInOperation = 3 * time.Second
 )
 
-func randStr(n int) string {
+func RandStr(n int) string {
 	b := make([]rune, n)
 	randMarker := rand.New(rand.NewSource(time.Now().UnixNano()))
 	for i := range b {
@@ -86,7 +86,7 @@ func randStr(n int) string {
 	return string(b)
 }
 
-func createFile(fileName, content string, c *C) {
+func CreateFile(fileName, content string, c *C) {
 	fout, err := os.Create(fileName)
 	defer fout.Close()
 	c.Assert(err, IsNil)
@@ -94,11 +94,11 @@ func createFile(fileName, content string, c *C) {
 	c.Assert(err, IsNil)
 }
 
-func randLowStr(n int) string {
-	return strings.ToLower(randStr(n))
+func RandLowStr(n int) string {
+	return strings.ToLower(RandStr(n))
 }
 
-func forceDeleteBucket(client *Client, bucketName string, c *C) {
+func ForceDeleteBucket(client *Client, bucketName string, c *C) {
 	bucket, err := client.Bucket(bucketName)
 	c.Assert(err, IsNil)
 
@@ -197,7 +197,7 @@ func (s *OssClientSuite) SetUpSuite(c *C) {
 	c.Assert(err, IsNil)
 
 	for _, bucket := range lbr.Buckets {
-		forceDeleteBucket(client, bucket.Name, c)
+		ForceDeleteBucket(client, bucket.Name, c)
 	}
 
 	testLogger.Println("test client started")
@@ -219,7 +219,7 @@ func (s *OssClientSuite) TearDownSuite(c *C) {
 }
 
 func (s *OssClientSuite) deleteBucket(client *Client, bucketName string, c *C) {
-	forceDeleteBucket(client, bucketName, c)
+	ForceDeleteBucket(client, bucketName, c)
 }
 
 // SetUpTest runs after each test or benchmark runs
@@ -232,7 +232,7 @@ func (s *OssClientSuite) TearDownTest(c *C) {
 
 // TestCreateBucket
 func (s *OssClientSuite) TestCreateBucket(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
@@ -298,7 +298,7 @@ func (s *OssClientSuite) TestCreateBucket(c *C) {
 
 	// Create bucket with configuration and test GetBucketInfo
 	for _, storage := range []StorageClassType{StorageStandard, StorageIA, StorageArchive, StorageColdArchive} {
-		bucketNameTest := bucketNamePrefix + randLowStr(6)
+		bucketNameTest := bucketNamePrefix + RandLowStr(6)
 		err = client.CreateBucket(bucketNameTest, StorageClass(storage), ACL(ACLPublicRead))
 		c.Assert(err, IsNil)
 		time.Sleep(timeoutInOperation)
@@ -320,7 +320,7 @@ func (s *OssClientSuite) TestCreateBucket(c *C) {
 
 	// Create bucket with configuration and test ListBuckets
 	for _, storage := range []StorageClassType{StorageStandard, StorageIA, StorageArchive, StorageColdArchive} {
-		bucketNameTest := bucketNamePrefix + randLowStr(6)
+		bucketNameTest := bucketNamePrefix + RandLowStr(6)
 		err = client.CreateBucket(bucketNameTest, StorageClass(storage))
 		c.Assert(err, IsNil)
 		time.Sleep(timeoutInOperation)
@@ -337,7 +337,7 @@ func (s *OssClientSuite) TestCreateBucket(c *C) {
 }
 
 func (s *OssClientSuite) TestCreateBucketRedundancyType(c *C) {
-	bucketNameTest := bucketNamePrefix + randLowStr(6)
+	bucketNameTest := bucketNamePrefix + RandLowStr(6)
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
@@ -397,14 +397,14 @@ func (s *OssClientSuite) TestCreateBucketNegative(c *C) {
 	testLogger.Println(err)
 
 	// ACL invalid
-	err = client.CreateBucket(bucketNamePrefix+randLowStr(6), ACL("InvaldAcl"))
+	err = client.CreateBucket(bucketNamePrefix+RandLowStr(6), ACL("InvaldAcl"))
 	c.Assert(err, NotNil)
 	testLogger.Println(err)
 }
 
 // TestDeleteBucket
 func (s *OssClientSuite) TestDeleteBucket(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
@@ -435,7 +435,7 @@ func (s *OssClientSuite) TestDeleteBucket(c *C) {
 
 // TestDeleteBucketNegative
 func (s *OssClientSuite) TestDeleteBucketNegative(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
@@ -472,7 +472,7 @@ func (s *OssClientSuite) TestDeleteBucketNegative(c *C) {
 
 // TestListBucket
 func (s *OssClientSuite) TestListBucket(c *C) {
-	var prefix = bucketNamePrefix + randLowStr(6)
+	var prefix = bucketNamePrefix + RandLowStr(6)
 	var bucketNameLbOne = prefix + "tlb1"
 	var bucketNameLbTwo = prefix + "tlb2"
 	var bucketNameLbThree = prefix + "tlb3"
@@ -521,7 +521,7 @@ func (s *OssClientSuite) TestListBucket(c *C) {
 
 // TestListBucket
 func (s *OssClientSuite) TestIsBucketExist(c *C) {
-	var prefix = bucketNamePrefix + randLowStr(6)
+	var prefix = bucketNamePrefix + RandLowStr(6)
 	var bucketNameLbOne = prefix + "tibe1"
 	var bucketNameLbTwo = prefix + "tibe11"
 	var bucketNameLbThree = prefix + "tibe111"
@@ -574,7 +574,7 @@ func (s *OssClientSuite) TestIsBucketExist(c *C) {
 
 // TestSetBucketAcl
 func (s *OssClientSuite) TestSetBucketAcl(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
@@ -620,7 +620,7 @@ func (s *OssClientSuite) TestSetBucketAcl(c *C) {
 
 // TestSetBucketAclNegative
 func (s *OssClientSuite) TestBucketAclNegative(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
@@ -638,7 +638,7 @@ func (s *OssClientSuite) TestBucketAclNegative(c *C) {
 
 // TestGetBucketAcl
 func (s *OssClientSuite) TestGetBucketAcl(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
@@ -684,7 +684,7 @@ func (s *OssClientSuite) TestGetBucketAcl(c *C) {
 
 // TestGetBucketAcl
 func (s *OssClientSuite) TestGetBucketLocation(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
@@ -702,7 +702,7 @@ func (s *OssClientSuite) TestGetBucketLocation(c *C) {
 
 // TestGetBucketLocationNegative
 func (s *OssClientSuite) TestGetBucketLocationNegative(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
@@ -718,7 +718,7 @@ func (s *OssClientSuite) TestGetBucketLocationNegative(c *C) {
 
 // TestSetBucketLifecycle
 func (s *OssClientSuite) TestSetBucketLifecycle(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 	var rule1 = BuildLifecycleRuleByDate("rule1", "one", true, 2015, 11, 11)
 	var rule2 = BuildLifecycleRuleByDays("rule2", "two", true, 3)
 
@@ -764,7 +764,7 @@ func (s *OssClientSuite) TestSetBucketLifecycle(c *C) {
 
 // TestSetBucketLifecycleNew
 func (s *OssClientSuite) TestSetBucketLifecycleNew(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
@@ -788,7 +788,7 @@ func (s *OssClientSuite) TestSetBucketLifecycleNew(c *C) {
 
 	//invalid value of CreatedBeforeDate
 	expiration = LifecycleExpiration{
-		CreatedBeforeDate: randStr(10),
+		CreatedBeforeDate: RandStr(10),
 	}
 	rule = LifecycleRule{
 		ID:         "rule1",
@@ -966,7 +966,7 @@ func (s *OssClientSuite) TestSetBucketLifecycleNew(c *C) {
 
 // TestSetBucketLifecycleAboutVersionObject
 func (s *OssClientSuite) TestSetBucketLifecycleAboutVersionObject(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
@@ -1017,7 +1017,7 @@ func (s *OssClientSuite) TestSetBucketLifecycleAboutVersionObject(c *C) {
 
 // TestDeleteBucketLifecycle
 func (s *OssClientSuite) TestDeleteBucketLifecycle(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 
 	var rule1 = BuildLifecycleRuleByDate("rule1", "one", true, 2015, 11, 11)
 	var rule2 = BuildLifecycleRuleByDays("rule2", "two", true, 3)
@@ -1062,7 +1062,7 @@ func (s *OssClientSuite) TestDeleteBucketLifecycle(c *C) {
 
 // TestSetBucketLifecycleNegative
 func (s *OssClientSuite) TestBucketLifecycleNegative(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 	var rules = []LifecycleRule{}
 
 	client, err := New(endpoint, accessID, accessKey)
@@ -1093,7 +1093,7 @@ func (s *OssClientSuite) TestBucketLifecycleNegative(c *C) {
 
 // TestSetBucketReferer
 func (s *OssClientSuite) TestSetBucketReferer(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 	var referers = []string{"http://www.aliyun.com", "https://www.aliyun.com"}
 
 	client, err := New(endpoint, accessID, accessKey)
@@ -1137,7 +1137,7 @@ func (s *OssClientSuite) TestSetBucketReferer(c *C) {
 
 // TestSetBucketRefererNegative
 func (s *OssClientSuite) TestBucketRefererNegative(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 	var referers = []string{""}
 
 	client, err := New(endpoint, accessID, accessKey)
@@ -1156,7 +1156,7 @@ func (s *OssClientSuite) TestBucketRefererNegative(c *C) {
 
 // TestSetBucketLogging
 func (s *OssClientSuite) TestSetBucketLogging(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 	var bucketNameTarget = bucketNameTest + "-target"
 
 	client, err := New(endpoint, accessID, accessKey)
@@ -1196,7 +1196,7 @@ func (s *OssClientSuite) TestSetBucketLogging(c *C) {
 
 // TestDeleteBucketLogging
 func (s *OssClientSuite) TestDeleteBucketLogging(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 	var bucketNameTarget = bucketNameTest + "-target"
 
 	client, err := New(endpoint, accessID, accessKey)
@@ -1255,7 +1255,7 @@ func (s *OssClientSuite) TestDeleteBucketLogging(c *C) {
 
 // TestSetBucketLoggingNegative
 func (s *OssClientSuite) TestSetBucketLoggingNegative(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 	var bucketNameTarget = bucketNameTest + "-target"
 
 	client, err := New(endpoint, accessID, accessKey)
@@ -1294,7 +1294,7 @@ func (s *OssClientSuite) TestSetBucketLoggingNegative(c *C) {
 
 // TestSetBucketWebsite
 func (s *OssClientSuite) TestSetBucketWebsite(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 	var indexWebsite = "myindex.html"
 	var errorWebsite = "myerror.html"
 
@@ -1349,7 +1349,7 @@ func (s *OssClientSuite) TestSetBucketWebsite(c *C) {
 
 // TestDeleteBucketWebsite
 func (s *OssClientSuite) TestDeleteBucketWebsite(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 	var indexWebsite = "myindex.html"
 	var errorWebsite = "myerror.html"
 
@@ -1397,7 +1397,7 @@ func (s *OssClientSuite) TestDeleteBucketWebsite(c *C) {
 
 // TestSetBucketWebsiteNegative
 func (s *OssClientSuite) TestSetBucketWebsiteNegative(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 	var indexWebsite = "myindex.html"
 	var errorWebsite = "myerror.html"
 
@@ -1447,7 +1447,7 @@ func (s *OssClientSuite) TestSetBucketWebsiteNegative(c *C) {
 
 // TestSetBucketWebsiteDetail
 func (s *OssClientSuite) TestSetBucketWebsiteDetail(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 	var indexWebsite = "myindex.html"
 	var errorWebsite = "myerror.html"
 
@@ -1675,7 +1675,7 @@ func (s *OssClientSuite) TestSetBucketWebsiteDetail(c *C) {
 
 // TestSetBucketWebsiteXml
 func (s *OssClientSuite) TestSetBucketWebsiteXml(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
@@ -1736,7 +1736,7 @@ func (s *OssClientSuite) TestSetBucketWebsiteXml(c *C) {
 
 // TestSetBucketCORS
 func (s *OssClientSuite) TestSetBucketCORS(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 	var rule1 = CORSRule{
 		AllowedOrigin: []string{"*"},
 		AllowedMethod: []string{"PUT", "GET", "POST"},
@@ -1830,7 +1830,7 @@ func (s *OssClientSuite) TestSetBucketCORS(c *C) {
 
 // TestDeleteBucketCORS
 func (s *OssClientSuite) TestDeleteBucketCORS(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 	var rule = CORSRule{
 		AllowedOrigin: []string{"*"},
 		AllowedMethod: []string{"PUT", "GET", "POST"},
@@ -1876,7 +1876,7 @@ func (s *OssClientSuite) TestDeleteBucketCORS(c *C) {
 
 // TestSetBucketCORSNegative
 func (s *OssClientSuite) TestSetBucketCORSNegative(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 	var rule = CORSRule{
 		AllowedOrigin: []string{"*"},
 		AllowedMethod: []string{"PUT", "GET", "POST"},
@@ -1933,7 +1933,7 @@ func (s *OssClientSuite) TestSetBucketCORSNegative(c *C) {
 
 // TestGetBucketInfo
 func (s *OssClientSuite) TestGetBucketInfo(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
@@ -1956,7 +1956,7 @@ func (s *OssClientSuite) TestGetBucketInfo(c *C) {
 
 // TestGetBucketInfoNegative
 func (s *OssClientSuite) TestGetBucketInfoNegative(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
@@ -1972,7 +1972,7 @@ func (s *OssClientSuite) TestGetBucketInfoNegative(c *C) {
 
 // TestEndpointFormat
 func (s *OssClientSuite) TestEndpointFormat(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 
 	// http://host
 	client, err := New(endpoint, accessID, accessKey)
@@ -2061,7 +2061,7 @@ func (s *OssClientSuite) _TestHTTPS(c *C) {
 
 // TestClientOption
 func (s *OssClientSuite) TestClientOption(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 
 	client, err := New(endpoint, accessID, accessKey, UseCname(true),
 		Timeout(11, 12), SecurityToken("token"), Proxy(proxyHost))
@@ -2105,7 +2105,7 @@ func (s *OssClientSuite) TestClientOption(c *C) {
 
 // TestProxy
 func (s *OssClientSuite) ProxyTestFunc(c *C, authVersion AuthVersionType, extraHeaders []string) {
-	bucketNameTest := bucketNamePrefix + randLowStr(6)
+	bucketNameTest := bucketNamePrefix + RandLowStr(6)
 	objectName := "体育/奥运/首金"
 	objectValue := "大江东去,浪淘尽,千古风流人物。 故垒西边,人道是、三国周郎赤壁。"
 
@@ -2197,9 +2197,9 @@ func (s *OssClientSuite) TestProxy(c *C) {
 
 // TestProxy for https endpoint
 func (s *OssClientSuite) TestHttpsEndpointProxy(c *C) {
-	bucketNameTest := bucketNamePrefix + randLowStr(6)
-	objectName := objectNamePrefix + randLowStr(6)
-	objectValue := randLowStr(100)
+	bucketNameTest := bucketNamePrefix + RandLowStr(6)
+	objectName := objectNamePrefix + RandLowStr(6)
+	objectValue := RandLowStr(100)
 
 	httpsEndPoint := ""
 	if strings.HasPrefix(endpoint, "http://") {
@@ -2259,7 +2259,7 @@ func (s *OssClientSuite) getBucket(buckets []BucketProperties, bucket string) (b
 }
 
 func (s *OssClientSuite) TestHttpLogNotSignUrl(c *C) {
-	logName := "." + string(os.PathSeparator) + "test-go-sdk-httpdebug.log" + randStr(5)
+	logName := "." + string(os.PathSeparator) + "test-go-sdk-httpdebug.log" + RandStr(5)
 	f, err := os.OpenFile(logName, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0660)
 	c.Assert(err, IsNil)
 
@@ -2268,7 +2268,7 @@ func (s *OssClientSuite) TestHttpLogNotSignUrl(c *C) {
 
 	client.Config.Logger = log.New(f, "", log.LstdFlags)
 
-	var testBucketName = bucketNamePrefix + randLowStr(6)
+	var testBucketName = bucketNamePrefix + RandLowStr(6)
 
 	// CreateBucket
 	err = client.CreateBucket(testBucketName)
@@ -2290,7 +2290,7 @@ func (s *OssClientSuite) TestHttpLogNotSignUrl(c *C) {
 }
 
 func (s *OssClientSuite) HttpLogSignUrlTestFunc(c *C, authVersion AuthVersionType, extraHeaders []string) {
-	logName := "." + string(os.PathSeparator) + "test-go-sdk-httpdebug-signurl.log" + randStr(5)
+	logName := "." + string(os.PathSeparator) + "test-go-sdk-httpdebug-signurl.log" + RandStr(5)
 	f, err := os.OpenFile(logName, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0660)
 	c.Assert(err, IsNil)
 
@@ -2303,7 +2303,7 @@ func (s *OssClientSuite) HttpLogSignUrlTestFunc(c *C, authVersion AuthVersionTyp
 	client.Config.AuthVersion = authVersion
 	client.Config.AdditionalHeaders = extraHeaders
 
-	var testBucketName = bucketNamePrefix + randLowStr(6)
+	var testBucketName = bucketNamePrefix + RandLowStr(6)
 
 	// CreateBucket
 	err = client.CreateBucket(testBucketName)
@@ -2314,8 +2314,8 @@ func (s *OssClientSuite) HttpLogSignUrlTestFunc(c *C, authVersion AuthVersionTyp
 	client.Config.Logger = log.New(f, "", log.LstdFlags)
 
 	bucket, _ := client.Bucket(testBucketName)
-	objectName := objectNamePrefix + randStr(8)
-	objectValue := randStr(20)
+	objectName := objectNamePrefix + RandStr(8)
+	objectValue := RandStr(20)
 
 	// Sign URL for put
 	str, err := bucket.SignURL(objectName, HTTPPut, 60)
@@ -2389,7 +2389,7 @@ func (s *OssClientSuite) TestBucketEncyptionError(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(5)
+	bucketName := bucketNamePrefix + RandLowStr(5)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 
@@ -2426,7 +2426,7 @@ func (s *OssClientSuite) TestBucketEncyptionPutAndGetAndDelete(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(5)
+	bucketName := bucketNamePrefix + RandLowStr(5)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 
@@ -2478,7 +2478,7 @@ func (s *OssClientSuite) TestBucketEncyptionPutObjectSuccess(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(5)
+	bucketName := bucketNamePrefix + RandLowStr(5)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 
@@ -2510,25 +2510,6 @@ func (s *OssClientSuite) TestBucketEncyptionPutObjectSuccess(c *C) {
 	c.Assert(bucketResult.BucketInfo.SseRule.KMSMasterKeyID, Equals, "")
 	c.Assert(bucketResult.BucketInfo.Versioning, Equals, "")
 
-	// put and get object success
-	//bucket, err := client.Bucket(bucketName)
-	//c.Assert(err, IsNil)
-
-	// put object success
-	//objectName := objectNamePrefix + randStr(8)
-	//context := randStr(100)
-	//err = bucket.PutObject(objectName, strings.NewReader(context))
-	//c.Assert(err, IsNil)
-
-	// get object success
-	//body, err := bucket.GetObject(objectName)
-	//c.Assert(err, IsNil)
-	//str, err := readBody(body)
-	//c.Assert(err, IsNil)
-	//body.Close()
-	//c.Assert(str, Equals, context)
-
-	//bucket.DeleteObject(objectName)
 	err = client.DeleteBucket(bucketName)
 	c.Assert(err, IsNil)
 }
@@ -2537,13 +2518,15 @@ func (s *OssClientSuite) TestBucketEncyptionPutObjectError(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(5)
+	bucketName := bucketNamePrefix + RandLowStr(5)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 
 	// SetBucketEncryption:KMS ,""
 	encryptionRule := ServerEncryptionRule{}
 	encryptionRule.SSEDefault.SSEAlgorithm = string(KMSAlgorithm)
+	kmsId := "123"
+	encryptionRule.SSEDefault.KMSMasterKeyID = kmsId
 
 	var responseHeader http.Header
 	err = client.SetBucketEncryption(bucketName, encryptionRule, GetResponseHeader(&responseHeader))
@@ -2566,7 +2549,7 @@ func (s *OssClientSuite) TestBucketEncyptionPutObjectError(c *C) {
 	c.Assert(err, IsNil)
 
 	c.Assert(bucketResult.BucketInfo.SseRule.SSEAlgorithm, Equals, "KMS")
-	c.Assert(bucketResult.BucketInfo.SseRule.KMSMasterKeyID, Equals, "")
+	c.Assert(bucketResult.BucketInfo.SseRule.KMSMasterKeyID, Equals, kmsId)
 	c.Assert(bucketResult.BucketInfo.Versioning, Equals, "")
 
 	// put and get object failure
@@ -2574,8 +2557,8 @@ func (s *OssClientSuite) TestBucketEncyptionPutObjectError(c *C) {
 	c.Assert(err, IsNil)
 
 	// put object failure
-	objectName := objectNamePrefix + randStr(8)
-	context := randStr(100)
+	objectName := objectNamePrefix + RandStr(8)
+	context := RandStr(100)
 	err = bucket.PutObject(objectName, strings.NewReader(context))
 	c.Assert(err, NotNil)
 
@@ -2587,7 +2570,7 @@ func (s *OssClientSuite) TestBucketTaggingOperation(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(5)
+	bucketName := bucketNamePrefix + RandLowStr(5)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 
@@ -2623,11 +2606,11 @@ func (s *OssClientSuite) TestListBucketsTagging(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName1 := bucketNamePrefix + randLowStr(5)
+	bucketName1 := bucketNamePrefix + RandLowStr(5)
 	err = client.CreateBucket(bucketName1)
 	c.Assert(err, IsNil)
 
-	bucketName2 := bucketNamePrefix + randLowStr(5)
+	bucketName2 := bucketNamePrefix + RandLowStr(5)
 	err = client.CreateBucket(bucketName2)
 	c.Assert(err, IsNil)
 
@@ -2651,7 +2634,7 @@ func (s *OssClientSuite) TestGetBucketStat(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(5)
+	bucketName := bucketNamePrefix + RandLowStr(5)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 
@@ -2659,12 +2642,12 @@ func (s *OssClientSuite) TestGetBucketStat(c *C) {
 	c.Assert(err, IsNil)
 
 	// put object
-	objectName := objectNamePrefix + randLowStr(5)
-	err = bucket.PutObject(objectName, strings.NewReader(randStr(10)))
+	objectName := objectNamePrefix + RandLowStr(5)
+	err = bucket.PutObject(objectName, strings.NewReader(RandStr(10)))
 	c.Assert(err, IsNil)
 
 	bucket.DeleteObject(objectName)
-	err = bucket.PutObject(objectName, strings.NewReader(randStr(10)))
+	err = bucket.PutObject(objectName, strings.NewReader(RandStr(10)))
 	c.Assert(err, IsNil)
 	bucket.DeleteObject(objectName)
 
@@ -2679,7 +2662,7 @@ func (s *OssBucketSuite) TestGetBucketVersioning(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(6)
+	bucketName := bucketNamePrefix + RandLowStr(6)
 
 	var respHeader http.Header
 	err = client.CreateBucket(bucketName, GetResponseHeader(&respHeader))
@@ -2697,14 +2680,14 @@ func (s *OssBucketSuite) TestGetBucketVersioning(c *C) {
 	c.Assert(versioningResult.Status, Equals, "Enabled")
 	c.Assert(GetRequestId(respHeader) != "", Equals, true)
 
-	forceDeleteBucket(client, bucketName, c)
+	ForceDeleteBucket(client, bucketName, c)
 }
 
 func (s *OssClientSuite) TestBucketPolicy(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(5)
+	bucketName := bucketNamePrefix + RandLowStr(5)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 
@@ -2753,7 +2736,7 @@ func (s *OssClientSuite) TestBucketPolicyNegative(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(5)
+	bucketName := bucketNamePrefix + RandLowStr(5)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 
@@ -2788,7 +2771,7 @@ func (s *OssClientSuite) TestBucketPolicyNegative(c *C) {
 	err = client.DeleteBucketPolicy(bucketName, GetResponseHeader(&responseHeader))
 	c.Assert(err, IsNil)
 
-	bucketNameEmpty := bucketNamePrefix + randLowStr(5)
+	bucketNameEmpty := bucketNamePrefix + RandLowStr(5)
 	client.DeleteBucket(bucketNameEmpty)
 
 	err = client.DeleteBucketPolicy(bucketNameEmpty, GetResponseHeader(&responseHeader))
@@ -2803,7 +2786,7 @@ func (s *OssClientSuite) TestSetBucketRequestPayment(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(5)
+	bucketName := bucketNamePrefix + RandLowStr(5)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 
@@ -2825,7 +2808,7 @@ func (s *OssClientSuite) TestSetBucketRequestPaymentNegative(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(5)
+	bucketName := bucketNamePrefix + RandLowStr(5)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 
@@ -2851,7 +2834,7 @@ func (s *OssClientSuite) TestBucketQos(c *C) {
 	c.Assert(err, IsNil)
 	testLogger.Println("QosInfo:", ret)
 
-	bucketName := bucketNamePrefix + randLowStr(5)
+	bucketName := bucketNamePrefix + RandLowStr(5)
 	_ = client.DeleteBucket(bucketName)
 
 	err = client.CreateBucket(bucketName)
@@ -2986,7 +2969,7 @@ func (testInfBuild *TestCredentialsProvider) GetCredentials() Credentials {
 }
 
 func (s *OssClientSuite) TestClientCredentialInfBuild(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 	var defaultBuild TestCredentialsProvider
 	client, err := New(endpoint, "", "", SetCredentialsProvider(&defaultBuild))
 	c.Assert(err, IsNil)
@@ -3004,7 +2987,7 @@ func (s *OssClientSuite) TestClientSetLocalIpError(c *C) {
 	client, err := New(endpoint, accessID, accessKey, SetLocalAddr(localTCPAddr))
 	c.Assert(err, IsNil)
 
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 	err = client.CreateBucket(bucketNameTest)
 	c.Assert(err, NotNil)
 }
@@ -3023,7 +3006,7 @@ func (s *OssClientSuite) TestClientSetLocalIpSuccess(c *C) {
 	client, err := New(endpoint, accessID, accessKey, SetLocalAddr(localTCPAddr))
 	c.Assert(err, IsNil)
 
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 	err = client.CreateBucket(bucketNameTest)
 	c.Assert(err, IsNil)
 	err = client.DeleteBucket(bucketNameTest)
@@ -3032,7 +3015,7 @@ func (s *OssClientSuite) TestClientSetLocalIpSuccess(c *C) {
 
 // TestCreateBucketInvalidName
 func (s *OssClientSuite) TestCreateBucketInvalidName(c *C) {
-	var bucketNameTest = "-" + bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = "-" + bucketNamePrefix + RandLowStr(6)
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 	// Create
@@ -3042,7 +3025,7 @@ func (s *OssClientSuite) TestCreateBucketInvalidName(c *C) {
 
 // TestClientProcessEndpointSuccess
 func (s *OssClientSuite) TestClientProcessEndpointSuccess(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 
 	testEndpoint := endpoint + "/" + "sina.com" + "?" + "para=abc"
 
@@ -3060,7 +3043,7 @@ func (s *OssClientSuite) TestClientProcessEndpointSuccess(c *C) {
 
 // TestClientProcessEndpointSuccess
 func (s *OssClientSuite) TestClientProcessEndpointError(c *C) {
-	var bucketNameTest = bucketNamePrefix + randLowStr(6)
+	var bucketNameTest = bucketNamePrefix + RandLowStr(6)
 
 	testEndpoint := "https://127.0.0.1/" + endpoint
 
@@ -3077,7 +3060,7 @@ func (s *OssClientSuite) TestClientBucketError(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := "-" + randLowStr(5)
+	bucketName := "-" + RandLowStr(5)
 	_, err = client.Bucket(bucketName)
 	c.Assert(err, NotNil)
 }
@@ -3086,7 +3069,7 @@ func (s *OssClientSuite) TestSetBucketInventory(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(5)
+	bucketName := bucketNamePrefix + RandLowStr(5)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 
@@ -3155,7 +3138,7 @@ func (s *OssClientSuite) TestBucketInventory(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(5)
+	bucketName := bucketNamePrefix + RandLowStr(5)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 
@@ -3281,7 +3264,7 @@ func (s *OssClientSuite) TestBucketInventoryNegative(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(5)
+	bucketName := bucketNamePrefix + RandLowStr(5)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 

+ 2 - 0
oss/conf.go

@@ -99,8 +99,10 @@ type Config struct {
 	UploadLimiter       *OssLimiter         // Bandwidth limit reader for upload
 	CredentialsProvider CredentialsProvider // User provides interface to get AccessKeyID, AccessKeySecret, SecurityToken
 	LocalAddr           net.Addr            // local client host info
+	UserSetUa           bool                // UserAgent is set by user or not
 	AuthVersion         AuthVersionType     //  v1 or v2 signature,default is v1
 	AdditionalHeaders   []string            //  special http headers needed to be sign
+	
 }
 
 // LimitUploadSpeed uploadSpeed:KB/s, 0 is unlimited,default is 0

+ 4 - 14
oss/conn.go

@@ -399,19 +399,9 @@ func (conn Conn) handleBody(req *http.Request, body io.Reader, initCRC uint64,
 	var file *os.File
 	var crc hash.Hash64
 	reader := body
-
-	// Length
-	switch v := body.(type) {
-	case *bytes.Buffer:
-		req.ContentLength = int64(v.Len())
-	case *bytes.Reader:
-		req.ContentLength = int64(v.Len())
-	case *strings.Reader:
-		req.ContentLength = int64(v.Len())
-	case *os.File:
-		req.ContentLength = tryGetFileSize(v)
-	case *io.LimitedReader:
-		req.ContentLength = int64(v.N)
+	readerLen, err := GetReaderLen(reader)
+	if err == nil {
+		req.ContentLength = readerLen
 	}
 	req.Header.Set(HTTPHeaderContentLength, strconv.FormatInt(req.ContentLength, 10))
 
@@ -424,7 +414,7 @@ func (conn Conn) handleBody(req *http.Request, body io.Reader, initCRC uint64,
 
 	// CRC
 	if reader != nil && conn.config.IsEnableCRC {
-		crc = NewCRC(crcTable(), initCRC)
+		crc = NewCRC(CrcTable(), initCRC)
 		reader = TeeReader(reader, crc, req.ContentLength, listener, tracker)
 	}
 

+ 3 - 3
oss/conn_test.go

@@ -117,13 +117,13 @@ func (s *OssConnSuite) TestAuth(c *C) {
 }
 
 func (s *OssConnSuite) TestConnToolFunc(c *C) {
-	err := checkRespCode(202, []int{})
+	err := CheckRespCode(202, []int{})
 	c.Assert(err, NotNil)
 
-	err = checkRespCode(202, []int{404})
+	err = CheckRespCode(202, []int{404})
 	c.Assert(err, NotNil)
 
-	err = checkRespCode(202, []int{202, 404})
+	err = CheckRespCode(202, []int{202, 404})
 	c.Assert(err, IsNil)
 
 	srvErr, err := serviceErrFromXML([]byte(""), 312, "")

+ 1 - 1
oss/const.go

@@ -218,7 +218,7 @@ const (
 
 	NullVersion = "null"
 
-	Version = "v2.0.8" // Go SDK version
+	Version = "v2.1.0" // Go SDK version
 )
 
 // FrameType

+ 4 - 4
oss/crc_test.go

@@ -222,7 +222,7 @@ func (s *OssCrcSuite) TestCRCRandomCombine(c *C) {
 
 // TestEnableCRCAndMD5 tests MD5 and CRC check
 func (s *OssCrcSuite) TestEnableCRCAndMD5(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
 	newFileName := "BingWallpaper-2015-11-07-2.jpg"
 	objectValue := "空山新雨后,天气晚来秋。明月松间照,清泉石上流。竹喧归浣女,莲动下渔舟。随意春芳歇,王孙自可留。"
@@ -319,7 +319,7 @@ func (s *OssCrcSuite) TestEnableCRCAndMD5(c *C) {
 
 // TestDisableCRCAndMD5 disables MD5 and CRC
 func (s *OssCrcSuite) TestDisableCRCAndMD5(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
 	newFileName := "BingWallpaper-2015-11-07-3.jpg"
 	objectValue := "中岁颇好道,晚家南山陲。兴来每独往,胜事空自知。行到水穷处,坐看云起时。偶然值林叟,谈笑无还期。"
@@ -415,7 +415,7 @@ func (s *OssCrcSuite) TestDisableCRCAndMD5(c *C) {
 
 // TestSpecifyContentMD5 specifies MD5
 func (s *OssCrcSuite) TestSpecifyContentMD5(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
 	objectValue := "积雨空林烟火迟,蒸藜炊黍饷东菑。漠漠水田飞白鹭,阴阴夏木啭黄鹂。山中习静观朝槿,松下清斋折露葵。野老与人争席罢,海鸥何事更相疑。"
 
@@ -480,7 +480,7 @@ func (s *OssCrcSuite) TestSpecifyContentMD5(c *C) {
 
 // TestAppendObjectNegative
 func (s *OssCrcSuite) TestAppendObjectNegative(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	objectValue := "空山不见人,但闻人语响。返影入深林,复照青苔上。"
 
 	nextPos, err := s.bucket.AppendObject(objectName, strings.NewReader(objectValue), 0, InitCRC(0))

+ 68 - 0
oss/crypto/aes_ctr.go

@@ -0,0 +1,68 @@
+package osscrypto
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"io"
+)
+
+type aesCtr struct {
+	encrypter cipher.Stream
+	decrypter cipher.Stream
+}
+
+func newAesCtr(cd CipherData) (Cipher, error) {
+	block, err := aes.NewCipher(cd.Key)
+	if err != nil {
+		return nil, err
+	}
+
+	encrypter := cipher.NewCTR(block, cd.IV)
+	decrypter := cipher.NewCTR(block, cd.IV)
+	return &aesCtr{encrypter, decrypter}, nil
+}
+
+func (c *aesCtr) Encrypt(src io.Reader) io.Reader {
+	reader := &ctrEncryptReader{
+		encrypter: c.encrypter,
+		src:       src,
+	}
+	return reader
+}
+
+type ctrEncryptReader struct {
+	encrypter cipher.Stream
+	src       io.Reader
+}
+
+func (reader *ctrEncryptReader) Read(data []byte) (int, error) {
+	plainText := make([]byte, len(data), len(data))
+	n, err := reader.src.Read(plainText)
+	if n > 0 {
+		plainText = plainText[0:n]
+		reader.encrypter.XORKeyStream(data, plainText)
+	}
+	return n, err
+}
+
+func (c *aesCtr) Decrypt(src io.Reader) io.Reader {
+	return &ctrDecryptReader{
+		decrypter: c.decrypter,
+		src:       src,
+	}
+}
+
+type ctrDecryptReader struct {
+	decrypter cipher.Stream
+	src       io.Reader
+}
+
+func (reader *ctrDecryptReader) Read(data []byte) (int, error) {
+	cryptoText := make([]byte, len(data), len(data))
+	n, err := reader.src.Read(cryptoText)
+	if n > 0 {
+		cryptoText = cryptoText[0:n]
+		reader.decrypter.XORKeyStream(data, cryptoText)
+	}
+	return n, err
+}

+ 153 - 0
oss/crypto/aes_ctr_cipher.go

@@ -0,0 +1,153 @@
+package osscrypto
+
+import (
+	"io"
+)
+
+const (
+	aesKeySize = 32
+	ivSize     = 16
+)
+
+// aesCtrCipherBuilder for building ContentCipher
+type aesCtrCipherBuilder struct {
+	MasterCipher MasterCipher
+}
+
+// aesCtrCipher will use aes ctr algorithm
+type aesCtrCipher struct {
+	CipherData CipherData
+	Cipher     Cipher
+}
+
+// CreateAesCtrCipher creates ContentCipherBuilder
+func CreateAesCtrCipher(cipher MasterCipher) ContentCipherBuilder {
+	return aesCtrCipherBuilder{MasterCipher: cipher}
+}
+
+// createCipherData create CipherData for encrypt object data
+func (builder aesCtrCipherBuilder) createCipherData() (CipherData, error) {
+	var cd CipherData
+	var err error
+	err = cd.RandomKeyIv(aesKeySize, ivSize)
+	if err != nil {
+		return cd, err
+	}
+
+	cd.WrapAlgorithm = builder.MasterCipher.GetWrapAlgorithm()
+	cd.CEKAlgorithm = AesCtrAlgorithm
+	cd.MatDesc = builder.MasterCipher.GetMatDesc()
+
+	// EncryptedKey
+	cd.EncryptedKey, err = builder.MasterCipher.Encrypt(cd.Key)
+	if err != nil {
+		return cd, err
+	}
+
+	// EncryptedIV
+	cd.EncryptedIV, err = builder.MasterCipher.Encrypt(cd.IV)
+	if err != nil {
+		return cd, err
+	}
+
+	return cd, nil
+}
+
+// contentCipherCD is used to create ContentCipher with CipherData
+func (builder aesCtrCipherBuilder) contentCipherCD(cd CipherData) (ContentCipher, error) {
+	cipher, err := newAesCtr(cd)
+	if err != nil {
+		return nil, err
+	}
+
+	return &aesCtrCipher{
+		CipherData: cd,
+		Cipher:     cipher,
+	}, nil
+}
+
+// ContentCipher is used to create ContentCipher interface
+func (builder aesCtrCipherBuilder) ContentCipher() (ContentCipher, error) {
+	cd, err := builder.createCipherData()
+	if err != nil {
+		return nil, err
+	}
+	return builder.contentCipherCD(cd)
+}
+
+// ContentCipherEnv is used to create a decrption ContentCipher from Envelope
+func (builder aesCtrCipherBuilder) ContentCipherEnv(envelope Envelope) (ContentCipher, error) {
+	var cd CipherData
+	cd.EncryptedKey = make([]byte, len(envelope.CipherKey))
+	copy(cd.EncryptedKey, []byte(envelope.CipherKey))
+
+	plainKey, err := builder.MasterCipher.Decrypt([]byte(envelope.CipherKey))
+	if err != nil {
+		return nil, err
+	}
+	cd.Key = make([]byte, len(plainKey))
+	copy(cd.Key, plainKey)
+
+	cd.EncryptedIV = make([]byte, len(envelope.IV))
+	copy(cd.EncryptedIV, []byte(envelope.IV))
+
+	plainIV, err := builder.MasterCipher.Decrypt([]byte(envelope.IV))
+	if err != nil {
+		return nil, err
+	}
+
+	cd.IV = make([]byte, len(plainIV))
+	copy(cd.IV, plainIV)
+
+	cd.MatDesc = envelope.MatDesc
+	cd.WrapAlgorithm = envelope.WrapAlg
+	cd.CEKAlgorithm = envelope.CEKAlg
+
+	return builder.contentCipherCD(cd)
+}
+
+// GetMatDesc is used to get MasterCipher's MatDesc
+func (builder aesCtrCipherBuilder) GetMatDesc() string {
+	return builder.MasterCipher.GetMatDesc()
+}
+
+// EncryptContents will generate a random key and iv and encrypt the data using ctr
+func (cc *aesCtrCipher) EncryptContent(src io.Reader) (io.ReadCloser, error) {
+	reader := cc.Cipher.Encrypt(src)
+	return &CryptoEncrypter{Body: src, Encrypter: reader}, nil
+}
+
+// DecryptContent is used to decrypt object using ctr
+func (cc *aesCtrCipher) DecryptContent(src io.Reader) (io.ReadCloser, error) {
+	reader := cc.Cipher.Decrypt(src)
+	return &CryptoDecrypter{Body: src, Decrypter: reader}, nil
+}
+
+// GetCipherData is used to get cipher data information
+func (cc *aesCtrCipher) GetCipherData() *CipherData {
+	return &(cc.CipherData)
+}
+
+// GetCipherData returns cipher data
+func (cc *aesCtrCipher) GetEncryptedLen(plainTextLen int64) int64 {
+	// AES CTR encryption mode does not change content length
+	return plainTextLen
+}
+
+// GetAlignLen is used to get align length
+func (cc *aesCtrCipher) GetAlignLen() int {
+	return len(cc.CipherData.IV)
+}
+
+// Clone is used to create a new aesCtrCipher from itself
+func (cc *aesCtrCipher) Clone(cd CipherData) (ContentCipher, error) {
+	cipher, err := newAesCtr(cd)
+	if err != nil {
+		return nil, err
+	}
+
+	return &aesCtrCipher{
+		CipherData: cd,
+		Cipher:     cipher,
+	}, nil
+}

+ 41 - 0
oss/crypto/aes_ctr_cipher_test.go

@@ -0,0 +1,41 @@
+package osscrypto
+
+import (
+	. "gopkg.in/check.v1"
+)
+
+func (s *OssCryptoBucketSuite) TestContentEncryptCipherError(c *C) {
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	cc, err := contentProvider.ContentCipher()
+	c.Assert(err, IsNil)
+
+	var cipherData CipherData
+	cipherData.RandomKeyIv(31, 15)
+
+	_, err = cc.Clone(cipherData)
+	c.Assert(err, NotNil)
+}
+
+func (s *OssCryptoBucketSuite) TestCreateCipherDataError(c *C) {
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, "", "")
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+
+	v := contentProvider.(aesCtrCipherBuilder)
+	_, err := v.createCipherData()
+	c.Assert(err, NotNil)
+}
+
+func (s *OssCryptoBucketSuite) TestContentCipherCDError(c *C) {
+	var cd CipherData
+
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, "", "")
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+
+	v := contentProvider.(aesCtrCipherBuilder)
+	_, err := v.contentCipherCD(cd)
+	c.Assert(err, NotNil)
+}

+ 69 - 0
oss/crypto/cipher.go

@@ -0,0 +1,69 @@
+package osscrypto
+
+import (
+	"io"
+)
+
+// Cipher is interface for encryption or decryption of an object
+type Cipher interface {
+	Encrypter
+	Decrypter
+}
+
+// Encrypter is interface with only encrypt method
+type Encrypter interface {
+	Encrypt(io.Reader) io.Reader
+}
+
+// Decrypter is interface with only decrypt method
+type Decrypter interface {
+	Decrypt(io.Reader) io.Reader
+}
+
+// CryptoEncrypter provides close method for Encrypter
+type CryptoEncrypter struct {
+	Body      io.Reader
+	Encrypter io.Reader
+	isClosed  bool
+}
+
+// Close lets the CryptoEncrypter satisfy io.ReadCloser interface
+func (rc *CryptoEncrypter) Close() error {
+	rc.isClosed = true
+	if closer, ok := rc.Body.(io.ReadCloser); ok {
+		return closer.Close()
+	}
+	return nil
+}
+
+// Read lets the CryptoEncrypter satisfy io.ReadCloser interface
+func (rc *CryptoEncrypter) Read(b []byte) (int, error) {
+	if rc.isClosed {
+		return 0, io.EOF
+	}
+	return rc.Encrypter.Read(b)
+}
+
+// CryptoDecrypter provides close method for Decrypter
+type CryptoDecrypter struct {
+	Body      io.Reader
+	Decrypter io.Reader
+	isClosed  bool
+}
+
+// Close lets the CryptoDecrypter satisfy io.ReadCloser interface
+func (rc *CryptoDecrypter) Close() error {
+	rc.isClosed = true
+	if closer, ok := rc.Body.(io.ReadCloser); ok {
+		return closer.Close()
+	}
+	return nil
+}
+
+// Read lets the CryptoDecrypter satisfy io.ReadCloser interface
+func (rc *CryptoDecrypter) Read(b []byte) (int, error) {
+	if rc.isClosed {
+		return 0, io.EOF
+	}
+	return rc.Decrypter.Read(b)
+}

+ 32 - 0
oss/crypto/cipher_test.go

@@ -0,0 +1,32 @@
+package osscrypto
+
+import (
+	"io"
+	"strings"
+
+	. "gopkg.in/check.v1"
+)
+
+func (s *OssCryptoBucketSuite) TestAesCtr(c *C) {
+	var cipherData CipherData
+	cipherData.RandomKeyIv(32, 16)
+	cipher, _ := newAesCtr(cipherData)
+
+	byteReader := strings.NewReader(RandLowStr(100))
+	enReader := cipher.Encrypt(byteReader)
+	encrypter := &CryptoEncrypter{Body: byteReader, Encrypter: enReader}
+	encrypter.Close()
+	buff := make([]byte, 10)
+	n, err := encrypter.Read(buff)
+	c.Assert(n, Equals, 0)
+	c.Assert(err, Equals, io.EOF)
+
+	deReader := cipher.Encrypt(byteReader)
+	Decrypter := &CryptoDecrypter{Body: byteReader, Decrypter: deReader}
+	Decrypter.Close()
+	buff = make([]byte, 10)
+	n, err = Decrypter.Read(buff)
+	c.Assert(n, Equals, 0)
+	c.Assert(err, Equals, io.EOF)
+
+}

+ 539 - 0
oss/crypto/crypto_bucket.go

@@ -0,0 +1,539 @@
+package osscrypto
+
+import (
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"hash"
+	"hash/crc64"
+	"io"
+	"net/http"
+	"os"
+	"strconv"
+
+	kms "github.com/aliyun/alibaba-cloud-sdk-go/services/kms"
+	"github.com/aliyun/aliyun-oss-go-sdk/oss"
+)
+
+// MasterCipherManager is interface for getting master key with MatDesc(material desc)
+// If you may use different master keys for encrypting and decrypting objects,each master
+// key must have a unique, non-emtpy, unalterable MatDesc(json string format) and you must provide this interface
+// If you always use the same master key for encrypting and decrypting objects, MatDesc
+// can be empty and you don't need to provide this interface
+//
+// matDesc map[string]string:is converted by matDesc json string
+// return: []string  the secret key information,such as {"rsa-public-key","rsa-private-key"} or {"non-rsa-key"}
+type MasterCipherManager interface {
+	GetMasterKey(matDesc map[string]string) ([]string, error)
+}
+
+// ExtraCipherBuilder is interface for creating a decrypt ContentCipher with Envelope
+// If the objects you need to decrypt are neither encrypted with ContentCipherBuilder
+// you provided, nor encrypted with rsa and ali kms master keys, you must provide this interface
+//
+// ContentCipher  the interface used to decrypt objects
+type ExtraCipherBuilder interface {
+	GetDecryptCipher(envelope Envelope, cm MasterCipherManager) (ContentCipher, error)
+}
+
+// CryptoBucketOption CryptoBucket option such as SetAliKmsClient, SetMasterCipherManager, SetDecryptCipherManager.
+type CryptoBucketOption func(*CryptoBucket)
+
+// SetAliKmsClient set field AliKmsClient of CryptoBucket
+// If the objects you need to decrypt are encrypted with ali kms master key,but not with ContentCipherBuilder
+// you provided, you must provide this interface
+func SetAliKmsClient(client *kms.Client) CryptoBucketOption {
+	return func(bucket *CryptoBucket) {
+		bucket.AliKmsClient = client
+	}
+}
+
+// SetMasterCipherManager set field MasterCipherManager of CryptoBucket
+func SetMasterCipherManager(manager MasterCipherManager) CryptoBucketOption {
+	return func(bucket *CryptoBucket) {
+		bucket.MasterCipherManager = manager
+	}
+}
+
+// SetExtraCipherBuilder set field ExtraCipherBuilder of CryptoBucket
+func SetExtraCipherBuilder(extraBuilder ExtraCipherBuilder) CryptoBucketOption {
+	return func(bucket *CryptoBucket) {
+		bucket.ExtraCipherBuilder = extraBuilder
+	}
+}
+
+// DefaultExtraCipherBuilder is Default implementation of the ExtraCipherBuilder for rsa and kms master keys
+type DefaultExtraCipherBuilder struct {
+	AliKmsClient *kms.Client
+}
+
+// GetDecryptCipher is used to get ContentCipher for decrypt object
+func (decb *DefaultExtraCipherBuilder) GetDecryptCipher(envelope Envelope, cm MasterCipherManager) (ContentCipher, error) {
+	if cm == nil {
+		return nil, fmt.Errorf("DefaultExtraCipherBuilder GetDecryptCipher error,MasterCipherManager is nil")
+	}
+
+	if envelope.CEKAlg != AesCtrAlgorithm {
+		return nil, fmt.Errorf("DefaultExtraCipherBuilder GetDecryptCipher error,not supported content algorithm %s", envelope.CEKAlg)
+	}
+
+	if envelope.WrapAlg != RsaCryptoWrap && envelope.WrapAlg != KmsAliCryptoWrap {
+		return nil, fmt.Errorf("DefaultExtraCipherBuilder GetDecryptCipher error,not supported envelope wrap algorithm %s", envelope.WrapAlg)
+	}
+
+	matDesc := make(map[string]string)
+	if envelope.MatDesc != "" {
+		err := json.Unmarshal([]byte(envelope.MatDesc), &matDesc)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	masterKeys, err := cm.GetMasterKey(matDesc)
+	if err != nil {
+		return nil, err
+	}
+
+	var contentCipher ContentCipher
+	if envelope.WrapAlg == RsaCryptoWrap {
+		// for rsa master key
+		if len(masterKeys) != 2 {
+			return nil, fmt.Errorf("rsa keys count must be 2,now is %d", len(masterKeys))
+		}
+		rsaCipher, err := CreateMasterRsa(matDesc, masterKeys[0], masterKeys[1])
+		if err != nil {
+			return nil, err
+		}
+		aesCtrBuilder := CreateAesCtrCipher(rsaCipher)
+		contentCipher, err = aesCtrBuilder.ContentCipherEnv(envelope)
+
+	} else if envelope.WrapAlg == KmsAliCryptoWrap {
+		// for kms master key
+		if len(masterKeys) != 1 {
+			return nil, fmt.Errorf("non-rsa keys count must be 1,now is %d", len(masterKeys))
+		}
+
+		if decb.AliKmsClient == nil {
+			return nil, fmt.Errorf("aliyun kms client is nil")
+		}
+
+		kmsCipher, err := CreateMasterAliKms(matDesc, masterKeys[0], decb.AliKmsClient)
+		if err != nil {
+			return nil, err
+		}
+		aesCtrBuilder := CreateAesCtrCipher(kmsCipher)
+		contentCipher, err = aesCtrBuilder.ContentCipherEnv(envelope)
+	} else {
+		// to do
+		// for master keys which are neither rsa nor kms
+	}
+
+	return contentCipher, err
+}
+
+// CryptoBucket implements the operations for encrypting and decrypting objects
+// ContentCipherBuilder is used to encrypt and decrypt objects by default
+// when the object's MatDesc which you want to decrypt is emtpy or same to the
+// master key's MatDesc you provided in ContentCipherBuilder, sdk try to
+// use ContentCipherBuilder to decrypt
+type CryptoBucket struct {
+	oss.Bucket
+	ContentCipherBuilder ContentCipherBuilder
+	ExtraCipherBuilder   ExtraCipherBuilder
+	MasterCipherManager  MasterCipherManager
+	AliKmsClient         *kms.Client
+}
+
+// GetCryptoBucket create a client encyrption bucket
+func GetCryptoBucket(client *oss.Client, bucketName string, builder ContentCipherBuilder,
+	options ...CryptoBucketOption) (*CryptoBucket, error) {
+	var cryptoBucket CryptoBucket
+	cryptoBucket.Client = *client
+	cryptoBucket.BucketName = bucketName
+	cryptoBucket.ContentCipherBuilder = builder
+
+	for _, option := range options {
+		option(&cryptoBucket)
+	}
+
+	if cryptoBucket.ExtraCipherBuilder == nil {
+		cryptoBucket.ExtraCipherBuilder = &DefaultExtraCipherBuilder{AliKmsClient: cryptoBucket.AliKmsClient}
+	}
+
+	return &cryptoBucket, nil
+}
+
+// PutObject creates a new object and encyrpt it on client side when uploading to oss
+func (bucket CryptoBucket) PutObject(objectKey string, reader io.Reader, options ...oss.Option) error {
+	options = bucket.AddEncryptionUaSuffix(options)
+	cc, err := bucket.ContentCipherBuilder.ContentCipher()
+	if err != nil {
+		return err
+	}
+
+	cryptoReader, err := cc.EncryptContent(reader)
+	if err != nil {
+		return err
+	}
+
+	var request *oss.PutObjectRequest
+	srcLen, err := oss.GetReaderLen(reader)
+	if err != nil {
+		request = &oss.PutObjectRequest{
+			ObjectKey: objectKey,
+			Reader:    cryptoReader,
+		}
+	} else {
+		encryptedLen := cc.GetEncryptedLen(srcLen)
+		request = &oss.PutObjectRequest{
+			ObjectKey: objectKey,
+			Reader:    oss.LimitReadCloser(cryptoReader, encryptedLen),
+		}
+	}
+
+	opts := addCryptoHeaders(options, cc.GetCipherData())
+	resp, err := bucket.DoPutObject(request, opts)
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+
+	return err
+}
+
+// GetObject downloads the object from oss
+// If the object is encrypted, sdk decrypt it automaticly
+func (bucket CryptoBucket) GetObject(objectKey string, options ...oss.Option) (io.ReadCloser, error) {
+	options = bucket.AddEncryptionUaSuffix(options)
+	result, err := bucket.DoGetObject(&oss.GetObjectRequest{ObjectKey: objectKey}, options)
+	if err != nil {
+		return nil, err
+	}
+	return result.Response, nil
+}
+
+// GetObjectToFile downloads the object from oss to local file
+// If the object is encrypted, sdk decrypt it automaticly
+func (bucket CryptoBucket) GetObjectToFile(objectKey, filePath string, options ...oss.Option) error {
+	options = bucket.AddEncryptionUaSuffix(options)
+	tempFilePath := filePath + oss.TempFileSuffix
+
+	// Calls the API to actually download the object. Returns the result instance.
+	result, err := bucket.DoGetObject(&oss.GetObjectRequest{ObjectKey: objectKey}, options)
+	if err != nil {
+		return err
+	}
+	defer result.Response.Close()
+
+	// If the local file does not exist, create a new one. If it exists, overwrite it.
+	fd, err := os.OpenFile(tempFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, oss.FilePermMode)
+	if err != nil {
+		return err
+	}
+
+	// Copy the data to the local file path.
+	_, err = io.Copy(fd, result.Response.Body)
+	fd.Close()
+	if err != nil {
+		return err
+	}
+
+	// Compares the CRC value
+	hasRange, _, _ := oss.IsOptionSet(options, oss.HTTPHeaderRange)
+	encodeOpt, _ := oss.FindOption(options, oss.HTTPHeaderAcceptEncoding, nil)
+	acceptEncoding := ""
+	if encodeOpt != nil {
+		acceptEncoding = encodeOpt.(string)
+	}
+	if bucket.GetConfig().IsEnableCRC && !hasRange && acceptEncoding != "gzip" {
+		result.Response.ClientCRC = result.ClientCRC.Sum64()
+		err = oss.CheckCRC(result.Response, "GetObjectToFile")
+		if err != nil {
+			os.Remove(tempFilePath)
+			return err
+		}
+	}
+
+	return os.Rename(tempFilePath, filePath)
+}
+
+// DoGetObject is the actual API that gets the encrypted or not encrypted object.
+// It's the internal function called by other public APIs.
+func (bucket CryptoBucket) DoGetObject(request *oss.GetObjectRequest, options []oss.Option) (*oss.GetObjectResult, error) {
+	options = bucket.AddEncryptionUaSuffix(options)
+
+	// first,we must head object
+	metaInfo, err := bucket.GetObjectDetailedMeta(request.ObjectKey)
+	if err != nil {
+		return nil, err
+	}
+
+	isEncryptedObj := isEncryptedObject(metaInfo)
+	if !isEncryptedObj {
+		return bucket.Bucket.DoGetObject(request, options)
+	}
+
+	envelope, err := getEnvelopeFromHeader(metaInfo)
+	if err != nil {
+		return nil, err
+	}
+
+	if !isValidContentAlg(envelope.CEKAlg) {
+		return nil, fmt.Errorf("not supported content algorithm %s,object:%s", envelope.CEKAlg, request.ObjectKey)
+	}
+
+	if !envelope.IsValid() {
+		return nil, fmt.Errorf("getEnvelopeFromHeader error,object:%s", request.ObjectKey)
+	}
+
+	// use ContentCipherBuilder to decrpt object by default
+	encryptMatDesc := bucket.ContentCipherBuilder.GetMatDesc()
+	var cc ContentCipher
+	err = nil
+	if envelope.MatDesc == encryptMatDesc {
+		cc, err = bucket.ContentCipherBuilder.ContentCipherEnv(envelope)
+	} else {
+		cc, err = bucket.ExtraCipherBuilder.GetDecryptCipher(envelope, bucket.MasterCipherManager)
+	}
+
+	if err != nil {
+		return nil, fmt.Errorf("%s,object:%s", err.Error(), request.ObjectKey)
+	}
+
+	discardFrontAlignLen := int64(0)
+	uRange, err := oss.GetRangeConfig(options)
+	if err != nil {
+		return nil, err
+	}
+
+	if uRange != nil && uRange.HasStart {
+		// process range to align key size
+		adjustStart := adjustRangeStart(uRange.Start, cc)
+		discardFrontAlignLen = uRange.Start - adjustStart
+		if discardFrontAlignLen > 0 {
+			uRange.Start = adjustStart
+			options = oss.DeleteOption(options, oss.HTTPHeaderRange)
+			options = append(options, oss.NormalizedRange(oss.GetRangeString(*uRange)))
+		}
+
+		// seek iv
+		cipherData := cc.GetCipherData().Clone()
+		cipherData.SeekIV(uint64(adjustStart))
+		cc, _ = cc.Clone(cipherData)
+	}
+
+	params, _ := oss.GetRawParams(options)
+	resp, err := bucket.Do("GET", request.ObjectKey, params, options, nil, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	result := &oss.GetObjectResult{
+		Response: resp,
+	}
+
+	// CRC
+	var crcCalc hash.Hash64
+	hasRange, _, _ := oss.IsOptionSet(options, oss.HTTPHeaderRange)
+	if bucket.GetConfig().IsEnableCRC && !hasRange {
+		crcCalc = crc64.New(oss.CrcTable())
+		result.ServerCRC = resp.ServerCRC
+		result.ClientCRC = crcCalc
+	}
+
+	// Progress
+	listener := oss.GetProgressListener(options)
+	contentLen, _ := strconv.ParseInt(resp.Headers.Get(oss.HTTPHeaderContentLength), 10, 64)
+	resp.Body = oss.TeeReader(resp.Body, crcCalc, contentLen, listener, nil)
+	resp.Body, err = cc.DecryptContent(resp.Body)
+	if err == nil && discardFrontAlignLen > 0 {
+		resp.Body = &oss.DiscardReadCloser{
+			RC:      resp.Body,
+			Discard: int(discardFrontAlignLen)}
+	}
+	return result, err
+}
+
+// PutObjectFromFile creates a new object from the local file
+// the object will be encrypted automaticly on client side when uploaded to oss
+func (bucket CryptoBucket) PutObjectFromFile(objectKey, filePath string, options ...oss.Option) error {
+	options = bucket.AddEncryptionUaSuffix(options)
+	fd, err := os.Open(filePath)
+	if err != nil {
+		return err
+	}
+	defer fd.Close()
+
+	opts := oss.AddContentType(options, filePath, objectKey)
+	cc, err := bucket.ContentCipherBuilder.ContentCipher()
+	if err != nil {
+		return err
+	}
+
+	cryptoReader, err := cc.EncryptContent(fd)
+	if err != nil {
+		return err
+	}
+
+	var request *oss.PutObjectRequest
+	srcLen, err := oss.GetReaderLen(fd)
+	if err != nil {
+		request = &oss.PutObjectRequest{
+			ObjectKey: objectKey,
+			Reader:    cryptoReader,
+		}
+	} else {
+		encryptedLen := cc.GetEncryptedLen(srcLen)
+		request = &oss.PutObjectRequest{
+			ObjectKey: objectKey,
+			Reader:    oss.LimitReadCloser(cryptoReader, encryptedLen),
+		}
+	}
+
+	opts = addCryptoHeaders(opts, cc.GetCipherData())
+	resp, err := bucket.DoPutObject(request, opts)
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+	return nil
+}
+
+// AppendObject please refer to Bucket.AppendObject
+func (bucket CryptoBucket) AppendObject(objectKey string, reader io.Reader, appendPosition int64, options ...oss.Option) (int64, error) {
+	return 0, fmt.Errorf("CryptoBucket doesn't support AppendObject")
+}
+
+// DoAppendObject please refer to Bucket.DoAppendObject
+func (bucket CryptoBucket) DoAppendObject(request *oss.AppendObjectRequest, options []oss.Option) (*oss.AppendObjectResult, error) {
+	return nil, fmt.Errorf("CryptoBucket doesn't support DoAppendObject")
+}
+
+// PutObjectWithURL please refer to Bucket.PutObjectWithURL
+func (bucket CryptoBucket) PutObjectWithURL(signedURL string, reader io.Reader, options ...oss.Option) error {
+	return fmt.Errorf("CryptoBucket doesn't support PutObjectWithURL")
+}
+
+// PutObjectFromFileWithURL please refer to Bucket.PutObjectFromFileWithURL
+func (bucket CryptoBucket) PutObjectFromFileWithURL(signedURL, filePath string, options ...oss.Option) error {
+	return fmt.Errorf("CryptoBucket doesn't support PutObjectFromFileWithURL")
+}
+
+// DoPutObjectWithURL please refer to Bucket.DoPutObjectWithURL
+func (bucket CryptoBucket) DoPutObjectWithURL(signedURL string, reader io.Reader, options []oss.Option) (*oss.Response, error) {
+	return nil, fmt.Errorf("CryptoBucket doesn't support DoPutObjectWithURL")
+}
+
+// GetObjectWithURL please refer to Bucket.GetObjectWithURL
+func (bucket CryptoBucket) GetObjectWithURL(signedURL string, options ...oss.Option) (io.ReadCloser, error) {
+	return nil, fmt.Errorf("CryptoBucket doesn't support GetObjectWithURL")
+}
+
+// GetObjectToFileWithURL please refer to Bucket.GetObjectToFileWithURL
+func (bucket CryptoBucket) GetObjectToFileWithURL(signedURL, filePath string, options ...oss.Option) error {
+	return fmt.Errorf("CryptoBucket doesn't support GetObjectToFileWithURL")
+}
+
+// DoGetObjectWithURL please refer to Bucket.DoGetObjectWithURL
+func (bucket CryptoBucket) DoGetObjectWithURL(signedURL string, options []oss.Option) (*oss.GetObjectResult, error) {
+	return nil, fmt.Errorf("CryptoBucket doesn't support DoGetObjectWithURL")
+}
+
+// ProcessObject please refer to Bucket.ProcessObject
+func (bucket CryptoBucket) ProcessObject(objectKey string, process string, options ...oss.Option) (oss.ProcessObjectResult, error) {
+	var out oss.ProcessObjectResult
+	return out, fmt.Errorf("CryptoBucket doesn't support ProcessObject")
+}
+
+func (bucket CryptoBucket) AddEncryptionUaSuffix(options []oss.Option) []oss.Option {
+	var outOption []oss.Option
+	bSet, _, _ := oss.IsOptionSet(options, oss.HTTPHeaderUserAgent)
+	if bSet || bucket.Client.Config.UserSetUa {
+		outOption = options
+		return outOption
+	}
+	outOption = append(options, oss.UserAgentHeader(bucket.Client.Config.UserAgent+"/"+EncryptionUaSuffix))
+	return outOption
+}
+
+// isEncryptedObject judge the object is encrypted or not
+func isEncryptedObject(headers http.Header) bool {
+	encrptedKey := headers.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionKey)
+	return len(encrptedKey) > 0
+}
+
+// addCryptoHeaders save Envelope information in oss meta
+func addCryptoHeaders(options []oss.Option, cd *CipherData) []oss.Option {
+	opts := []oss.Option{}
+
+	// convert content-md5
+	md5Option, _ := oss.FindOption(options, oss.HTTPHeaderContentMD5, nil)
+	if md5Option != nil {
+		opts = append(opts, oss.Meta(OssClientSideEncryptionUnencryptedContentMD5, md5Option.(string)))
+		options = oss.DeleteOption(options, oss.HTTPHeaderContentMD5)
+	}
+
+	// convert content-length
+	lenOption, _ := oss.FindOption(options, oss.HTTPHeaderContentLength, nil)
+	if lenOption != nil {
+		opts = append(opts, oss.Meta(OssClientSideEncryptionUnencryptedContentLength, lenOption.(string)))
+		options = oss.DeleteOption(options, oss.HTTPHeaderContentLength)
+	}
+
+	opts = append(opts, options...)
+
+	// matDesc
+	if cd.MatDesc != "" {
+		opts = append(opts, oss.Meta(OssClientSideEncryptionMatDesc, cd.MatDesc))
+	}
+
+	// encrypted key
+	strEncryptedKey := base64.StdEncoding.EncodeToString(cd.EncryptedKey)
+	opts = append(opts, oss.Meta(OssClientSideEncryptionKey, strEncryptedKey))
+
+	// encrypted iv
+	strEncryptedIV := base64.StdEncoding.EncodeToString(cd.EncryptedIV)
+	opts = append(opts, oss.Meta(OssClientSideEncryptionStart, strEncryptedIV))
+
+	// wrap alg
+	opts = append(opts, oss.Meta(OssClientSideEncryptionWrapAlg, cd.WrapAlgorithm))
+
+	// cek alg
+	opts = append(opts, oss.Meta(OssClientSideEncryptionCekAlg, cd.CEKAlgorithm))
+
+	return opts
+}
+
+func getEnvelopeFromHeader(header http.Header) (Envelope, error) {
+	var envelope Envelope
+	envelope.IV = header.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionStart)
+	decodedIV, err := base64.StdEncoding.DecodeString(envelope.IV)
+	if err != nil {
+		return envelope, err
+	}
+	envelope.IV = string(decodedIV)
+
+	envelope.CipherKey = header.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionKey)
+	decodedKey, err := base64.StdEncoding.DecodeString(envelope.CipherKey)
+	if err != nil {
+		return envelope, err
+	}
+	envelope.CipherKey = string(decodedKey)
+
+	envelope.MatDesc = header.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionMatDesc)
+	envelope.WrapAlg = header.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionWrapAlg)
+	envelope.CEKAlg = header.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionCekAlg)
+	envelope.UnencryptedMD5 = header.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionUnencryptedContentMD5)
+	envelope.UnencryptedContentLen = header.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionUnencryptedContentLength)
+	return envelope, err
+}
+
+func isValidContentAlg(algName string) bool {
+	// now content encyrption only support aec/ctr algorithm
+	return algName == AesCtrAlgorithm
+}
+
+func adjustRangeStart(start int64, cc ContentCipher) int64 {
+	alignLen := int64(cc.GetAlignLen())
+	return (start / alignLen) * alignLen
+}

+ 1226 - 0
oss/crypto/crypto_bucket_test.go

@@ -0,0 +1,1226 @@
+package osscrypto
+
+import (
+	"crypto/md5"
+	"encoding/hex"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"log"
+	"math/rand"
+	math_rand "math/rand"
+	"net/http"
+	"os"
+	"strings"
+	"testing"
+	"time"
+
+	kms "github.com/aliyun/alibaba-cloud-sdk-go/services/kms"
+	"github.com/aliyun/aliyun-oss-go-sdk/oss"
+	. "gopkg.in/check.v1"
+)
+
+func Test(t *testing.T) {
+	TestingT(t)
+}
+
+type OssCryptoBucketSuite struct {
+}
+
+var _ = Suite(&OssCryptoBucketSuite{})
+
+var (
+	matDesc = make(map[string]string)
+
+	rsaPublicKey string = `-----BEGIN PUBLIC KEY-----
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCokfiAVXXf5ImFzKDw+XO/UByW
+6mse2QsIgz3ZwBtMNu59fR5zttSx+8fB7vR4CN3bTztrP9A6bjoN0FFnhlQ3vNJC
+5MFO1PByrE/MNd5AAfSVba93I6sx8NSk5MzUCA4NJzAUqYOEWGtGBcom6kEF6MmR
+1EKib1Id8hpooY5xaQIDAQAB
+-----END PUBLIC KEY-----`
+
+	rsaPrivateKey string = `-----BEGIN PRIVATE KEY-----
+MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKiR+IBVdd/kiYXM
+oPD5c79QHJbqax7ZCwiDPdnAG0w27n19HnO21LH7x8Hu9HgI3dtPO2s/0DpuOg3Q
+UWeGVDe80kLkwU7U8HKsT8w13kAB9JVtr3cjqzHw1KTkzNQIDg0nMBSpg4RYa0YF
+yibqQQXoyZHUQqJvUh3yGmihjnFpAgMBAAECgYA49RmCQ14QyKevDfVTdvYlLmx6
+kbqgMbYIqk+7w611kxoCTMR9VMmJWgmk/Zic9mIAOEVbd7RkCdqT0E+xKzJJFpI2
+ZHjrlwb21uqlcUqH1Gn+wI+jgmrafrnKih0kGucavr/GFi81rXixDrGON9KBE0FJ
+cPVdc0XiQAvCBnIIAQJBANXu3htPH0VsSznfqcDE+w8zpoAJdo6S/p30tcjsDQnx
+l/jYV4FXpErSrtAbmI013VYkdJcghNSLNUXppfk2e8UCQQDJt5c07BS9i2SDEXiz
+byzqCfXVzkdnDj9ry9mba1dcr9B9NCslVelXDGZKvQUBqNYCVxg398aRfWlYDTjU
+IoVVAkAbTyjPN6R4SkC4HJMg5oReBmvkwFCAFsemBk0GXwuzD0IlJAjXnAZ+/rIO
+ItewfwXIL1Mqz53lO/gK+q6TR585AkB304KUIoWzjyF3JqLP3IQOxzns92u9EV6l
+V2P+CkbMPXiZV6sls6I4XppJXX2i3bu7iidN3/dqJ9izQK94fMU9AkBZvgsIPCot
+y1/POIbv9LtnviDKrmpkXgVQSU4BmTPvXwTJm8APC7P/horSh3SVf1zgmnsyjm9D
+hO92gGc+4ajL
+-----END PRIVATE KEY-----`
+
+	rsaPublicKeyPks1 string = `-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAKiR+IBVdd/kiYXMoPD5c79QHJbqax7ZCwiDPdnAG0w27n19HnO21LH7
+x8Hu9HgI3dtPO2s/0DpuOg3QUWeGVDe80kLkwU7U8HKsT8w13kAB9JVtr3cjqzHw
+1KTkzNQIDg0nMBSpg4RYa0YFyibqQQXoyZHUQqJvUh3yGmihjnFpAgMBAAE=
+-----END RSA PUBLIC KEY-----`
+
+	rsaPrivateKeyPks1 string = `-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQCokfiAVXXf5ImFzKDw+XO/UByW6mse2QsIgz3ZwBtMNu59fR5z
+ttSx+8fB7vR4CN3bTztrP9A6bjoN0FFnhlQ3vNJC5MFO1PByrE/MNd5AAfSVba93
+I6sx8NSk5MzUCA4NJzAUqYOEWGtGBcom6kEF6MmR1EKib1Id8hpooY5xaQIDAQAB
+AoGAOPUZgkNeEMinrw31U3b2JS5sepG6oDG2CKpPu8OtdZMaAkzEfVTJiVoJpP2Y
+nPZiADhFW3e0ZAnak9BPsSsySRaSNmR465cG9tbqpXFKh9Rp/sCPo4Jq2n65yood
+JBrnGr6/xhYvNa14sQ6xjjfSgRNBSXD1XXNF4kALwgZyCAECQQDV7t4bTx9FbEs5
+36nAxPsPM6aACXaOkv6d9LXI7A0J8Zf42FeBV6RK0q7QG5iNNd1WJHSXIITUizVF
+6aX5NnvFAkEAybeXNOwUvYtkgxF4s28s6gn11c5HZw4/a8vZm2tXXK/QfTQrJVXp
+VwxmSr0FAajWAlcYN/fGkX1pWA041CKFVQJAG08ozzekeEpAuByTIOaEXgZr5MBQ
+gBbHpgZNBl8Lsw9CJSQI15wGfv6yDiLXsH8FyC9TKs+d5Tv4Cvquk0efOQJAd9OC
+lCKFs48hdyaiz9yEDsc57PdrvRFepVdj/gpGzD14mVerJbOiOF6aSV19ot27u4on
+Td/3aifYs0CveHzFPQJAWb4LCDwqLctfzziG7/S7Z74gyq5qZF4FUElOAZkz718E
+yZvADwuz/4aK0od0lX9c4Jp7Mo5vQ4TvdoBnPuGoyw==
+-----END RSA PRIVATE KEY-----`
+)
+
+var (
+	// Endpoint/ID/Key
+	endpoint         = os.Getenv("OSS_TEST_ENDPOINT")
+	accessID         = os.Getenv("OSS_TEST_ACCESS_KEY_ID")
+	accessKey        = os.Getenv("OSS_TEST_ACCESS_KEY_SECRET")
+	kmsID            = os.Getenv("OSS_TEST_KMS_ID")
+	kmsRegion        = os.Getenv("OSS_TEST_KMS_REGION")
+	kmsAccessID      = accessID
+	kmsAccessKey     = accessKey
+	bucketNamePrefix = "go-sdk-test-bucket-"
+	objectNamePrefix = "go-sdk-test-object-"
+)
+
+var (
+	logPath            = "go_sdk_test_" + time.Now().Format("20060102_150405") + ".log"
+	testLogFile, _     = os.OpenFile(logPath, os.O_RDWR|os.O_CREATE, 0664)
+	testLogger         = log.New(testLogFile, "", log.Ldate|log.Ltime|log.Lshortfile)
+	letters            = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
+	timeoutInOperation = 3 * time.Second
+)
+
+func RandStr(n int) string {
+	b := make([]rune, n)
+	randMarker := rand.New(rand.NewSource(time.Now().UnixNano()))
+	for i := range b {
+		b[i] = letters[randMarker.Intn(len(letters))]
+	}
+	return string(b)
+}
+
+func RandLowStr(n int) string {
+	return strings.ToLower(RandStr(n))
+}
+
+func GetFileMD5(filePath string) (string, error) {
+	fd, err := os.Open(filePath)
+	if err != nil {
+		return "", err
+	}
+	defer fd.Close()
+
+	md5 := md5.New()
+	_, err = io.Copy(md5, fd)
+	if err != nil {
+		return "", fmt.Errorf("buff copy error")
+	}
+	md5Str := hex.EncodeToString(md5.Sum(nil))
+	return md5Str, nil
+}
+
+func GetStringMd5(s string) string {
+	md5 := md5.New()
+	md5.Write([]byte(s))
+	md5Str := hex.EncodeToString(md5.Sum(nil))
+	return md5Str
+}
+
+func ForceDeleteBucket(client *oss.Client, bucketName string, c *C) {
+	bucket, err := client.Bucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// Delete Object
+	marker := oss.Marker("")
+	for {
+		lor, err := bucket.ListObjects(marker)
+		c.Assert(err, IsNil)
+		for _, object := range lor.Objects {
+			err = bucket.DeleteObject(object.Key)
+			c.Assert(err, IsNil)
+		}
+		marker = oss.Marker(lor.NextMarker)
+		if !lor.IsTruncated {
+			break
+		}
+	}
+
+	// Delete Object Versions and DeleteMarks
+	keyMarker := oss.KeyMarker("")
+	versionIdMarker := oss.VersionIdMarker("")
+	options := []oss.Option{keyMarker, versionIdMarker}
+	for {
+		lor, err := bucket.ListObjectVersions(options...)
+		if err != nil {
+			break
+		}
+
+		for _, object := range lor.ObjectDeleteMarkers {
+			err = bucket.DeleteObject(object.Key, oss.VersionId(object.VersionId))
+			c.Assert(err, IsNil)
+		}
+
+		for _, object := range lor.ObjectVersions {
+			err = bucket.DeleteObject(object.Key, oss.VersionId(object.VersionId))
+			c.Assert(err, IsNil)
+		}
+
+		keyMarker = oss.KeyMarker(lor.NextKeyMarker)
+		versionIdMarker := oss.VersionIdMarker(lor.NextVersionIdMarker)
+		options = []oss.Option{keyMarker, versionIdMarker}
+
+		if !lor.IsTruncated {
+			break
+		}
+	}
+
+	// Delete Part
+	keyMarker = oss.KeyMarker("")
+	uploadIDMarker := oss.UploadIDMarker("")
+	for {
+		lmur, err := bucket.ListMultipartUploads(keyMarker, uploadIDMarker)
+		c.Assert(err, IsNil)
+		for _, upload := range lmur.Uploads {
+			var imur = oss.InitiateMultipartUploadResult{Bucket: bucketName,
+				Key: upload.Key, UploadID: upload.UploadID}
+			err = bucket.AbortMultipartUpload(imur)
+			c.Assert(err, IsNil)
+		}
+		keyMarker = oss.KeyMarker(lmur.NextKeyMarker)
+		uploadIDMarker = oss.UploadIDMarker(lmur.NextUploadIDMarker)
+		if !lmur.IsTruncated {
+			break
+		}
+	}
+
+	// delete live channel
+	strMarker := ""
+	for {
+		result, err := bucket.ListLiveChannel(oss.Marker(strMarker))
+		c.Assert(err, IsNil)
+
+		for _, channel := range result.LiveChannel {
+			err := bucket.DeleteLiveChannel(channel.Name)
+			c.Assert(err, IsNil)
+		}
+
+		if result.IsTruncated {
+			strMarker = result.NextMarker
+		} else {
+			break
+		}
+	}
+
+	// Delete Bucket
+	err = client.DeleteBucket(bucketName)
+	c.Assert(err, IsNil)
+}
+
+func ReadBody(body io.ReadCloser) (string, error) {
+	data, err := ioutil.ReadAll(body)
+	body.Close()
+	if err != nil {
+		return "", err
+	}
+	return string(data), nil
+}
+
+// SetUpSuite runs once when the suite starts running
+func (s *OssCryptoBucketSuite) SetUpSuite(c *C) {
+}
+
+// TearDownSuite runs before each test or benchmark starts running
+func (s *OssCryptoBucketSuite) TearDownSuite(c *C) {
+}
+
+// SetUpTest runs after each test or benchmark runs
+func (s *OssCryptoBucketSuite) SetUpTest(c *C) {
+}
+
+// TearDownTest runs once after all tests or benchmarks have finished running
+func (s *OssCryptoBucketSuite) TearDownTest(c *C) {
+
+}
+
+func (s *OssCryptoBucketSuite) TestPutObjectNormalPks8(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	testMatDesc := make(map[string]string)
+	testMatDesc["desc"] = "test rsa key"
+	masterRsaCipher, _ := CreateMasterRsa(testMatDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	objectValue := RandStr(1023)
+
+	// Put string
+	var respHeader http.Header
+	err = bucket.PutObject(objectName, strings.NewReader(objectValue), oss.GetResponseHeader(&respHeader))
+	c.Assert(err, IsNil)
+
+	// Check
+	body, err := bucket.GetObject(objectName)
+	c.Assert(err, IsNil)
+	text, err := ReadBody(body)
+	c.Assert(text, Equals, objectValue)
+
+	// non-crypto bucket download
+	normalBucket, err := client.Bucket(bucketName)
+	c.Assert(err, IsNil)
+	body, err = normalBucket.GetObject(objectName)
+	c.Assert(err, IsNil)
+	encryptText, err := ReadBody(body)
+	c.Assert(encryptText != objectValue, Equals, true)
+
+	// acl
+	acl, err := bucket.GetObjectACL(objectName)
+	c.Assert(err, IsNil)
+	c.Assert(acl.ACL, Equals, "default")
+
+	err = bucket.DeleteObject(objectName)
+	c.Assert(err, IsNil)
+
+	// put with meta
+	options := []oss.Option{
+		oss.ObjectACL(oss.ACLPublicRead),
+		oss.Meta("myprop", "mypropval"),
+	}
+	err = bucket.PutObject(objectName, strings.NewReader(objectValue), options...)
+	c.Assert(err, IsNil)
+
+	// Check
+	body, err = bucket.GetObject(objectName)
+	c.Assert(err, IsNil)
+	text, err = ReadBody(body)
+	c.Assert(err, IsNil)
+	c.Assert(text, Equals, objectValue)
+
+	acl, err = bucket.GetObjectACL(objectName)
+	c.Assert(err, IsNil)
+	c.Assert(acl.ACL, Equals, string(oss.ACLPublicRead))
+
+	meta, err := bucket.GetObjectDetailedMeta(objectName)
+	c.Assert(err, IsNil)
+	c.Assert(meta.Get("X-Oss-Meta-Myprop"), Equals, "mypropval")
+
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestPutObjectNormalPks1(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKeyPks1, rsaPrivateKeyPks1)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	objectValue := RandStr(1023)
+
+	// Put string
+	var respHeader http.Header
+	err = bucket.PutObject(objectName, strings.NewReader(objectValue), oss.GetResponseHeader(&respHeader))
+	c.Assert(err, IsNil)
+
+	// Check
+	body, err := bucket.GetObject(objectName)
+	c.Assert(err, IsNil)
+	text, err := ReadBody(body)
+	c.Assert(text, Equals, objectValue)
+
+	// non-crypto bucket download
+	normalBucket, err := client.Bucket(bucketName)
+	c.Assert(err, IsNil)
+	body, err = normalBucket.GetObject(objectName)
+	c.Assert(err, IsNil)
+	encryptText, err := ReadBody(body)
+	c.Assert(encryptText != objectValue, Equals, true)
+
+	// acl
+	acl, err := bucket.GetObjectACL(objectName)
+	c.Assert(err, IsNil)
+	c.Assert(acl.ACL, Equals, "default")
+
+	err = bucket.DeleteObject(objectName)
+	c.Assert(err, IsNil)
+
+	// put with meta
+	options := []oss.Option{
+		oss.ObjectACL(oss.ACLPublicRead),
+		oss.Meta("myprop", "mypropval"),
+	}
+	err = bucket.PutObject(objectName, strings.NewReader(objectValue), options...)
+	c.Assert(err, IsNil)
+
+	// Check
+	body, err = bucket.GetObject(objectName)
+	c.Assert(err, IsNil)
+	text, err = ReadBody(body)
+	c.Assert(err, IsNil)
+	c.Assert(text, Equals, objectValue)
+
+	acl, err = bucket.GetObjectACL(objectName)
+	c.Assert(err, IsNil)
+	c.Assert(acl.ACL, Equals, string(oss.ACLPublicRead))
+
+	meta, err := bucket.GetObjectDetailedMeta(objectName)
+	c.Assert(err, IsNil)
+	c.Assert(meta.Get("X-Oss-Meta-Myprop"), Equals, "mypropval")
+
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestPutObjectEmptyPks1(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKeyPks1, rsaPrivateKeyPks1)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	objectValue := ""
+
+	// Put empty string
+	var respHeader http.Header
+	err = bucket.PutObject(objectName, strings.NewReader(objectValue), oss.GetResponseHeader(&respHeader))
+	c.Assert(err, IsNil)
+
+	// Check
+	body, err := bucket.GetObject(objectName)
+	c.Assert(err, IsNil)
+	text, err := ReadBody(body)
+	c.Assert(text, Equals, objectValue)
+
+	// non-crypto bucket download
+	normalBucket, err := client.Bucket(bucketName)
+	c.Assert(err, IsNil)
+	body, err = normalBucket.GetObject(objectName)
+	c.Assert(err, IsNil)
+	encryptText, err := ReadBody(body)
+	c.Assert(encryptText == objectValue, Equals, true)
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestPutObjectSmallSizePks1(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKeyPks1, rsaPrivateKeyPks1)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	objectValue := "123"
+
+	var respHeader http.Header
+	err = bucket.PutObject(objectName, strings.NewReader(objectValue), oss.GetResponseHeader(&respHeader))
+	c.Assert(err, IsNil)
+
+	// Check
+	body, err := bucket.GetObject(objectName)
+	c.Assert(err, IsNil)
+	text, err := ReadBody(body)
+	c.Assert(text, Equals, objectValue)
+
+	// non-crypto bucket download
+	normalBucket, err := client.Bucket(bucketName)
+	c.Assert(err, IsNil)
+	body, err = normalBucket.GetObject(objectName)
+	c.Assert(err, IsNil)
+	encryptText, err := ReadBody(body)
+	c.Assert(encryptText != objectValue, Equals, true)
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestPutObjectEmptyFilePks1(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKeyPks1, rsaPrivateKeyPks1)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	fileName := "oss-go-sdk-test-file-" + RandStr(5)
+	fo, err := os.Create(fileName)
+	c.Assert(err, IsNil)
+	_, err = fo.Write([]byte(""))
+	c.Assert(err, IsNil)
+	fo.Close()
+
+	objectName := objectNamePrefix + RandStr(8)
+
+	// file not exist
+	err = bucket.PutObjectFromFile(objectName, "/root1/abc.txt")
+	c.Assert(err, NotNil)
+
+	err = bucket.PutObjectFromFile(objectName, fileName)
+	c.Assert(err, IsNil)
+
+	downFileName := fileName + "-down"
+
+	// Check
+	err = bucket.GetObjectToFile(objectName, downFileName)
+	c.Assert(err, IsNil)
+
+	b1, err := ioutil.ReadFile(fileName)
+	b2, err := ioutil.ReadFile(downFileName)
+	c.Assert(len(b1), Equals, 0)
+	c.Assert(string(b1), Equals, string(b2))
+
+	os.Remove(downFileName)
+	os.Remove(fileName)
+
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestKmsPutObjectNormal(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	kmsClient, err := kms.NewClientWithAccessKey(kmsRegion, kmsAccessID, kmsAccessKey)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	masterKmsCipher, _ := CreateMasterAliKms(matDesc, kmsID, kmsClient)
+	contentProvider := CreateAesCtrCipher(masterKmsCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	objectValue := RandStr(1023)
+
+	// Put string
+	var respHeader http.Header
+	err = bucket.PutObject(objectName, strings.NewReader(objectValue), oss.GetResponseHeader(&respHeader))
+	c.Assert(err, IsNil)
+
+	// Check
+	body, err := bucket.GetObject(objectName)
+	c.Assert(err, IsNil)
+	text, err := ReadBody(body)
+	c.Assert(text, Equals, objectValue)
+
+	// non-crypto bucket download
+	normalBucket, err := client.Bucket(bucketName)
+	c.Assert(err, IsNil)
+	body, err = normalBucket.GetObject(objectName)
+	c.Assert(err, IsNil)
+	encryptText, err := ReadBody(body)
+	c.Assert(encryptText != objectValue, Equals, true)
+
+	// acl
+	acl, err := bucket.GetObjectACL(objectName)
+	c.Assert(err, IsNil)
+	c.Assert(acl.ACL, Equals, "default")
+
+	err = bucket.DeleteObject(objectName)
+	c.Assert(err, IsNil)
+
+	// put with meta
+	options := []oss.Option{
+		oss.ObjectACL(oss.ACLPublicRead),
+		oss.Meta("myprop", "mypropval"),
+	}
+	err = bucket.PutObject(objectName, strings.NewReader(objectValue), options...)
+	c.Assert(err, IsNil)
+
+	// Check
+	body, err = bucket.GetObject(objectName)
+	c.Assert(err, IsNil)
+	text, err = ReadBody(body)
+	c.Assert(err, IsNil)
+	c.Assert(text, Equals, objectValue)
+
+	acl, err = bucket.GetObjectACL(objectName)
+	c.Assert(err, IsNil)
+	c.Assert(acl.ACL, Equals, string(oss.ACLPublicRead))
+
+	meta, err := bucket.GetObjectDetailedMeta(objectName)
+	c.Assert(err, IsNil)
+	c.Assert(meta.Get("X-Oss-Meta-Myprop"), Equals, "mypropval")
+
+	// put object error,bucket not exist
+	bucket.BucketName = bucket.BucketName + "-not-exist"
+	err = bucket.PutObject(objectName, strings.NewReader(objectValue), options...)
+	c.Assert(err, NotNil)
+
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+type MockKmsManager struct {
+}
+
+func (mg *MockKmsManager) GetMasterKey(matDesc map[string]string) ([]string, error) {
+	if len(matDesc) == 0 {
+		return nil, fmt.Errorf("not found")
+	}
+
+	keyList := []string{kmsID}
+	return keyList, nil
+}
+
+func (s *OssCryptoBucketSuite) TestRsaBucketDecrptObjectWithKmsSuccess(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	kmsClient, err := kms.NewClientWithAccessKey(kmsRegion, kmsAccessID, kmsAccessKey)
+	c.Assert(err, IsNil)
+
+	// crypto bucket with kms
+	testMatDesc := make(map[string]string)
+	testMatDesc["desc"] = "test kms wrap"
+	masterKmsCipher, _ := CreateMasterAliKms(testMatDesc, kmsID, kmsClient)
+	contentProvider := CreateAesCtrCipher(masterKmsCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	objectValue := RandStr(1023)
+
+	// Put string
+	var respHeader http.Header
+	err = bucket.PutObject(objectName, strings.NewReader(objectValue), oss.GetResponseHeader(&respHeader))
+	c.Assert(err, IsNil)
+
+	// crypto bucket with rsa
+	var masterManager MockKmsManager
+	var options []CryptoBucketOption
+	options = append(options, SetAliKmsClient(kmsClient))
+	options = append(options, SetMasterCipherManager(&masterManager))
+
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	rsaProvider := CreateAesCtrCipher(masterRsaCipher)
+	rsaBucket, err := GetCryptoBucket(client, bucketName, rsaProvider, options...)
+
+	// Check
+	body, err := rsaBucket.GetObject(objectName)
+	c.Assert(err, IsNil)
+	text, err := ReadBody(body)
+	c.Assert(text, Equals, objectValue)
+
+	// non-crypto bucket download
+	normalBucket, err := client.Bucket(bucketName)
+	c.Assert(err, IsNil)
+	body, err = normalBucket.GetObject(objectName)
+	c.Assert(err, IsNil)
+	encryptText, err := ReadBody(body)
+	c.Assert(encryptText != objectValue, Equals, true)
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestRsaBucketDecrptObjectWithKmsError(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	kmsClient, err := kms.NewClientWithAccessKey(kmsRegion, kmsAccessID, kmsAccessKey)
+	c.Assert(err, IsNil)
+
+	// crypto bucket with kms
+	testMatDesc := make(map[string]string)
+	testMatDesc["desc"] = "test kms wrap"
+	masterKmsCipher, _ := CreateMasterAliKms(testMatDesc, kmsID, kmsClient)
+	contentProvider := CreateAesCtrCipher(masterKmsCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	objectValue := RandStr(1023)
+
+	// Put string
+	var respHeader http.Header
+	err = bucket.PutObject(objectName, strings.NewReader(objectValue), oss.GetResponseHeader(&respHeader))
+	c.Assert(err, IsNil)
+
+	// crypto bucket with rsa
+	var masterManager MockKmsManager
+	var options []CryptoBucketOption
+
+	// kms client is nil
+	//options = append(options, SetAliKmsClient(kmsClient))
+
+	options = append(options, SetMasterCipherManager(&masterManager))
+
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	rsaProvider := CreateAesCtrCipher(masterRsaCipher)
+	rsaBucket, err := GetCryptoBucket(client, bucketName, rsaProvider, options...)
+
+	// Check
+	_, err = rsaBucket.GetObject(objectName)
+	c.Assert(err, NotNil)
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestRangeGetObject(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	contentLen := 1024 * 1024
+	content := RandStr(contentLen)
+	err = bucket.PutObject(objectName, strings.NewReader(content))
+	c.Assert(err, IsNil)
+
+	// range get
+	for i := 0; i < 100; i++ {
+		math_rand.Seed(time.Now().UnixNano())
+		rangeStart := rand.Intn(contentLen)
+		rangeEnd := rangeStart + rand.Intn(contentLen-rangeStart)
+		if rangeEnd == rangeStart {
+			continue
+		}
+
+		body, err := bucket.GetObject(objectName, oss.Range(int64(rangeStart), int64(rangeEnd)))
+		c.Assert(err, IsNil)
+		downText, err := ReadBody(body)
+		c.Assert(len(downText) > 0, Equals, true)
+		downMd5 := GetStringMd5(downText)
+
+		srcText := content[rangeStart : rangeEnd+1]
+		srcMd5 := GetStringMd5(srcText)
+
+		c.Assert(len(downText), Equals, len(srcText))
+		c.Assert(downMd5, Equals, srcMd5)
+	}
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestGetNormalObject(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	// normal bucket
+	normalBucket, _ := client.Bucket(bucketName)
+
+	objectName := objectNamePrefix + RandStr(8)
+	objectValue := RandStr(1023)
+
+	// Put string
+	err = normalBucket.PutObject(objectName, strings.NewReader(objectValue))
+	c.Assert(err, IsNil)
+
+	// Check
+	body, err := bucket.GetObject(objectName)
+	c.Assert(err, IsNil)
+	text, err := ReadBody(body)
+	c.Assert(text, Equals, objectValue)
+
+	// delete object
+	err = bucket.DeleteObject(objectName)
+	c.Assert(err, IsNil)
+
+	// get object again
+	body, err = bucket.GetObject(objectName)
+	c.Assert(err, NotNil)
+
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestGetCryptoBucketNotSupport(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	objectValue := RandStr(1023)
+
+	// AppendObject
+	_, err = bucket.AppendObject(objectName, strings.NewReader(objectValue), 0)
+	c.Assert(err, NotNil)
+
+	// DoAppendObject
+	var request oss.AppendObjectRequest
+	var options []oss.Option
+	_, err = bucket.DoAppendObject(&request, options)
+	c.Assert(err, NotNil)
+
+	// PutObjectWithURL
+	err = bucket.PutObjectWithURL("oss://bucket/object", strings.NewReader(objectValue))
+	c.Assert(err, NotNil)
+
+	// PutObjectFromFileWithURL
+	err = bucket.PutObjectFromFileWithURL("oss://bucket/object", "file.txt")
+	c.Assert(err, NotNil)
+
+	// DoPutObjectWithURL
+	_, err = bucket.DoPutObjectWithURL("oss://bucket/object", strings.NewReader(objectValue), options)
+	c.Assert(err, NotNil)
+
+	// GetObjectWithURL
+	_, err = bucket.GetObjectWithURL("oss://bucket/object")
+	c.Assert(err, NotNil)
+
+	// GetObjectToFileWithURL
+	err = bucket.GetObjectToFileWithURL("oss://bucket/object", "file.txt")
+	c.Assert(err, NotNil)
+
+	// DoGetObjectWithURL
+	_, err = bucket.DoGetObjectWithURL("oss://bucket/object", options)
+	c.Assert(err, NotNil)
+
+	// ProcessObject
+	_, err = bucket.ProcessObject("oss://bucket/object", "")
+	c.Assert(err, NotNil)
+
+	// DownloadFile
+	err = bucket.DownloadFile(objectName, "file.txt", 1024)
+	c.Assert(err, NotNil)
+
+	// CopyFile
+	err = bucket.CopyFile("src-bucket", "src-object", "dest-object", 1024)
+	c.Assert(err, NotNil)
+
+	// UploadFile
+	err = bucket.UploadFile(objectName, "file.txt", 1024)
+	c.Assert(err, NotNil)
+}
+
+type MockRsaManager struct {
+}
+
+func (mg *MockRsaManager) GetMasterKey(matDesc map[string]string) ([]string, error) {
+	if len(matDesc) == 0 {
+		return nil, fmt.Errorf("not found")
+	}
+
+	keyList := []string{rsaPublicKey, rsaPrivateKey}
+	return keyList, nil
+}
+
+func (s *OssCryptoBucketSuite) TestGetMasterKey(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	testMatDesc := make(map[string]string)
+	testMatDesc["desc"] = "test rsa key"
+	masterRsaCipher, _ := CreateMasterRsa(testMatDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+
+	fileName := "../../sample/BingWallpaper-2015-11-07.jpg"
+	srcMD5, err := GetFileMD5(fileName)
+	c.Assert(err, IsNil)
+
+	err = bucket.PutObjectFromFile(objectName, fileName)
+	c.Assert(err, IsNil)
+
+	// other crypto bucket
+	var rsaManager MockRsaManager
+	masterRsaCipherOther, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProviderOther := CreateAesCtrCipher(masterRsaCipherOther)
+	bucketOther, err := GetCryptoBucket(client, bucketName, contentProviderOther, SetMasterCipherManager(&rsaManager))
+
+	//  download
+	downfileName := "test-go-sdk-file-" + RandLowStr(5) + ".jpg"
+	err = bucketOther.GetObjectToFile(objectName, downfileName)
+	c.Assert(err, IsNil)
+	downFileMD5, err := GetFileMD5(downfileName)
+	c.Assert(err, IsNil)
+	c.Assert(downFileMD5, Equals, srcMD5)
+
+	// GetObjectToFile error
+	err = bucketOther.GetObjectToFile(objectName, "/root1/"+downfileName)
+	c.Assert(err, NotNil)
+
+	os.Remove(downfileName)
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+type MockReader struct {
+	Reader io.Reader
+}
+
+func (r *MockReader) Read(b []byte) (int, error) {
+	return r.Reader.Read(b)
+}
+
+func (s *OssCryptoBucketSuite) TestPutObjectUnkownReaderLen(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	objectValue := RandStr(1023)
+
+	srcMD5 := GetStringMd5(objectValue)
+	options := []oss.Option{oss.ContentMD5(srcMD5), oss.ContentLength(1023)}
+
+	// Put string
+	mockReader := &MockReader{strings.NewReader(objectValue)}
+	err = bucket.PutObject(objectName, mockReader, options...)
+	c.Assert(err, IsNil)
+
+	// Check
+	body, err := bucket.GetObject(objectName)
+	c.Assert(err, IsNil)
+	text, err := ReadBody(body)
+	c.Assert(text, Equals, objectValue)
+
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestGetDecryptCipher(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	var rsaManager MockRsaManager
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider, SetMasterCipherManager(&rsaManager))
+
+	objectName := objectNamePrefix + RandStr(8)
+	objectValue := RandStr(1023)
+
+	// Put string
+	var respHeader http.Header
+	err = bucket.PutObject(objectName, strings.NewReader(objectValue), oss.GetResponseHeader(&respHeader))
+	c.Assert(err, IsNil)
+
+	// first,we must head object
+	metaInfo, err := bucket.GetObjectDetailedMeta(objectName)
+	c.Assert(err, IsNil)
+
+	envelope, _ := getEnvelopeFromHeader(metaInfo)
+
+	// test for getEnvelopeFromHeader
+	metaInfo.Set(oss.HTTPHeaderOssMetaPrefix+OssClientSideEncryptionKey, string([]byte{200, 200, 200}))
+	_, err = getEnvelopeFromHeader(metaInfo)
+	c.Assert(err, NotNil)
+	metaInfo.Set(oss.HTTPHeaderOssMetaPrefix+OssClientSideEncryptionKey, envelope.CipherKey)
+
+	metaInfo.Set(oss.HTTPHeaderOssMetaPrefix+OssClientSideEncryptionStart, string([]byte{200, 200, 200}))
+	_, err = getEnvelopeFromHeader(metaInfo)
+	c.Assert(err, NotNil)
+	metaInfo.Set(oss.HTTPHeaderOssMetaPrefix+OssClientSideEncryptionKey, envelope.IV)
+
+	// test for getDecryptCipher
+	CEKAlg := envelope.CEKAlg
+	envelope.CEKAlg = ""
+	_, err = bucket.ExtraCipherBuilder.GetDecryptCipher(envelope, bucket.MasterCipherManager)
+	c.Assert(err, NotNil)
+	envelope.CEKAlg = CEKAlg
+
+	// matDesc is emtpy
+	bucket.MasterCipherManager = &MockRsaManager{}
+	_, err = bucket.ExtraCipherBuilder.GetDecryptCipher(envelope, bucket.MasterCipherManager)
+	c.Assert(err, NotNil)
+
+	// MasterCipherManager is nil
+	bucket.MasterCipherManager = nil
+	_, err = bucket.ExtraCipherBuilder.GetDecryptCipher(envelope, bucket.MasterCipherManager)
+	c.Assert(err, NotNil)
+
+	WrapAlg := envelope.WrapAlg
+	envelope.WrapAlg = "test"
+	_, err = bucket.ExtraCipherBuilder.GetDecryptCipher(envelope, bucket.MasterCipherManager)
+	c.Assert(err, NotNil)
+	envelope.WrapAlg = WrapAlg
+
+	envelope.WrapAlg = KmsAliCryptoWrap
+	_, err = bucket.ExtraCipherBuilder.GetDecryptCipher(envelope, bucket.MasterCipherManager)
+	c.Assert(err, NotNil)
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestGetObjectEncryptedByCppRsa(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// put object encrypted by cpp
+	bucket, err := client.Bucket(bucketName)
+	c.Assert(err, IsNil)
+
+	objectName := objectNamePrefix + RandStr(8)
+	srcJpgFile := "../../sample/test-client-encryption-src.jpg"
+	fileEncryptedByCpp := "../../sample/test-client-encryption-crypto-cpp-rsa.jpg"
+
+	opts := []oss.Option{}
+	opts = append(opts, oss.Meta(OssClientSideEncryptionKey, "nyXOp7delQ/MQLjKQMhHLaT0w7u2yQoDLkSnK8MFg/MwYdh4na4/LS8LLbLcM18m8I/ObWUHU775I50sJCpdv+f4e0jLeVRRiDFWe+uo7Puc9j4xHj8YB3QlcIOFQiTxHIB6q+C+RA6lGwqqYVa+n3aV5uWhygyv1MWmESurppg="))
+	opts = append(opts, oss.Meta(OssClientSideEncryptionStart, "De/S3T8wFjx7QPxAAFl7h7TeI2EsZlfCwox4WhLGng5DK2vNXxULmulMUUpYkdc9umqmDilgSy5Z3Foafw+v4JJThfw68T/9G2gxZLrQTbAlvFPFfPM9Ehk6cY4+8WpY32uN8w5vrHyoSZGr343NxCUGIp6fQ9sSuOLMoJg7hNw="))
+	opts = append(opts, oss.Meta(OssClientSideEncryptionWrapAlg, "RSA/NONE/PKCS1Padding"))
+	opts = append(opts, oss.Meta(OssClientSideEncryptionCekAlg, "AES/CTR/NoPadding"))
+	err = bucket.PutObjectFromFile(objectName, fileEncryptedByCpp, opts...)
+	c.Assert(err, IsNil)
+
+	// download with crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	cryptoBucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	downFileName := "oss-go-sdk-test-file-" + RandStr(5)
+	err = cryptoBucket.GetObjectToFile(objectName, downFileName)
+	c.Assert(err, IsNil)
+
+	downMd5, _ := GetFileMD5(downFileName)
+	srcJpgMd5, _ := GetFileMD5(srcJpgFile)
+	c.Assert(downMd5, Equals, srcJpgMd5)
+	os.Remove(downFileName)
+
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestGetObjectEncryptedByPythonRsa(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// put object encrypted by python
+	bucket, err := client.Bucket(bucketName)
+	c.Assert(err, IsNil)
+
+	objectName := objectNamePrefix + RandStr(8)
+	srcJpgFile := "../../sample/test-client-encryption-src.jpg"
+	fileEncryptedByCpp := "../../sample/test-client-encryption-crypto-python-rsa.jpg"
+
+	opts := []oss.Option{}
+	opts = append(opts, oss.Meta(OssClientSideEncryptionKey, "ZNQM4g+JykUfOBMkfL8kbvChD3R23UH53sRyTg42h9H2ph8ZJJlo2tSP5Oi3nR5gJAwA/OTrruNq02M2Zt4N7zVWdbFArKbY/CkHpihVYOqsSU4Z8RmrNBm4QfC5om2WElRHNt8hlqhnvzhdorGDB5OoMQ8KvQqXDC53aM5OY64="))
+	opts = append(opts, oss.Meta(OssClientSideEncryptionStart, "mZ6kts6kaMm++0akhQQZl+tj8gPWznZ+giHciCQTIzriwBzZZO4d85YZeBStuUPshdnO3QHK63/NH9QFL6pwpLiXI9UZxkGygkp82oB4jaF4HKoQ4ujd670pXLxpljBLnp0sCxiCIaf5Fzp4jgNCurXycY10/5DN7yPPtdw7dkk="))
+	opts = append(opts, oss.Meta(OssClientSideEncryptionWrapAlg, "RSA/NONE/PKCS1Padding"))
+	opts = append(opts, oss.Meta(OssClientSideEncryptionCekAlg, "AES/CTR/NoPadding"))
+	err = bucket.PutObjectFromFile(objectName, fileEncryptedByCpp, opts...)
+	c.Assert(err, IsNil)
+
+	// download with crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	cryptoBucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	downFileName := "oss-go-sdk-test-file-" + RandStr(5)
+	err = cryptoBucket.GetObjectToFile(objectName, downFileName)
+	c.Assert(err, IsNil)
+
+	downMd5, _ := GetFileMD5(downFileName)
+	srcJpgMd5, _ := GetFileMD5(srcJpgFile)
+	c.Assert(downMd5, Equals, srcJpgMd5)
+	os.Remove(downFileName)
+
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestRepeatedPutObjectFromFile(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	objectName := objectNamePrefix + RandStr(8)
+	srcJpgFile := "../../sample/test-client-encryption-src.jpg"
+
+	// put object from file
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	cryptoBucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	err = cryptoBucket.PutObjectFromFile(objectName, srcJpgFile)
+	c.Assert(err, IsNil)
+
+	downFileName := "oss-go-sdk-test-file-" + RandStr(5)
+	err = cryptoBucket.GetObjectToFile(objectName, downFileName)
+	c.Assert(err, IsNil)
+
+	srcJpgMd5, _ := GetFileMD5(srcJpgFile)
+	downMd5, _ := GetFileMD5(downFileName)
+	c.Assert(len(srcJpgMd5) > 0, Equals, true)
+	c.Assert(len(downMd5) > 0, Equals, true)
+	c.Assert(downMd5, Equals, srcJpgMd5)
+	os.Remove(downFileName)
+
+	err = cryptoBucket.PutObjectFromFile(objectName+"-other", srcJpgFile)
+	c.Assert(err, IsNil)
+	err = cryptoBucket.GetObjectToFile(objectName, downFileName)
+	c.Assert(err, IsNil)
+	downMd5, _ = GetFileMD5(downFileName)
+	c.Assert(downMd5, Equals, srcJpgMd5)
+
+	os.Remove(downFileName)
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestPutObjectEncryptionUserAgent(c *C) {
+	logName := "." + string(os.PathSeparator) + "test-go-sdk.log" + RandStr(5)
+	f, err := os.OpenFile(logName, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0660)
+	c.Assert(err, IsNil)
+
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+	client.Config.LogLevel = oss.Debug
+	client.Config.Logger = log.New(f, "", log.LstdFlags)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	objectName := objectNamePrefix + RandStr(8)
+	srcJpgFile := "../../sample/test-client-encryption-src.jpg"
+
+	// put object from file
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	cryptoBucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	err = cryptoBucket.PutObjectFromFile(objectName, srcJpgFile)
+	c.Assert(err, IsNil)
+
+	// read log file,get http info
+	contents, err := ioutil.ReadFile(logName)
+	c.Assert(err, IsNil)
+
+	httpContent := string(contents)
+	c.Assert(strings.Contains(httpContent, EncryptionUaSuffix), Equals, true)
+
+    f.Close()
+	os.Remove(logName)
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestPutObjectNormalUserAgent(c *C) {
+	logName := "." + string(os.PathSeparator) + "test-go-sdk.log" + RandStr(5)
+	f, err := os.OpenFile(logName, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0660)
+	c.Assert(err, IsNil)
+
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+	client.Config.LogLevel = oss.Debug
+	client.Config.Logger = log.New(f, "", log.LstdFlags)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	objectName := objectNamePrefix + RandStr(8)
+	srcJpgFile := "../../sample/test-client-encryption-src.jpg"
+
+	bucket, err := client.Bucket(bucketName)
+
+	err = bucket.PutObjectFromFile(objectName, srcJpgFile)
+	c.Assert(err, IsNil)
+
+	// read log file,get http info
+	contents, err := ioutil.ReadFile(logName)
+	c.Assert(err, IsNil)
+
+	httpContent := string(contents)
+	c.Assert(strings.Contains(httpContent, EncryptionUaSuffix), Equals, false)
+
+    f.Close()
+	os.Remove(logName)
+	ForceDeleteBucket(client, bucketName, c)
+}

+ 26 - 0
oss/crypto/crypto_const.go

@@ -0,0 +1,26 @@
+package osscrypto
+
+// for client sider encryption oss meta
+const (
+	OssClientSideEncryptionKey                      string = "client-side-encryption-key"
+	OssClientSideEncryptionStart                           = "client-side-encryption-start"
+	OssClientSideEncryptionCekAlg                          = "client-side-encryption-cek-alg"
+	OssClientSideEncryptionWrapAlg                         = "client-side-encryption-wrap-alg"
+	OssClientSideEncryptionMatDesc                         = "client-side-encryption-matdesc"
+	OssClientSideEncryptionUnencryptedContentLength        = "client-side-encryption-unencrypted-content-length"
+	OssClientSideEncryptionUnencryptedContentMD5           = "client-side-encryption-unencrypted-content-md5"
+	OssClientSideEncryptionDataSize                        = "client-side-encryption-data-size"
+	OssClientSideEncryptionPartSize                        = "client-side-encryption-part-size"
+)
+
+// encryption Algorithm
+const (
+	RsaCryptoWrap    string = "RSA/NONE/PKCS1Padding"
+	KmsAliCryptoWrap string = "KMS/ALICLOUD"
+	AesCtrAlgorithm  string = "AES/CTR/NoPadding"
+)
+
+// user agent tag for client encryption
+const (
+	EncryptionUaSuffix string = "OssEncryptionClient"
+)

+ 12 - 0
oss/crypto/crypto_download.go

@@ -0,0 +1,12 @@
+package osscrypto
+
+import (
+	"fmt"
+
+	"github.com/aliyun/aliyun-oss-go-sdk/oss"
+)
+
+// DownloadFile with multi part mode, temporarily not supported
+func (bucket CryptoBucket) DownloadFile(objectKey, filePath string, partSize int64, options ...oss.Option) error {
+	return fmt.Errorf("CryptoBucket doesn't support DownloadFile")
+}

+ 12 - 0
oss/crypto/crypto_multicopy.go

@@ -0,0 +1,12 @@
+package osscrypto
+
+import (
+	"fmt"
+
+	"github.com/aliyun/aliyun-oss-go-sdk/oss"
+)
+
+// CopyFile with multi part mode, temporarily not supported
+func (bucket CryptoBucket) CopyFile(srcBucketName, srcObjectKey, destObjectKey string, partSize int64, options ...oss.Option) error {
+	return fmt.Errorf("CryptoBucket doesn't support CopyFile")
+}

+ 174 - 0
oss/crypto/crypto_multipart.go

@@ -0,0 +1,174 @@
+package osscrypto
+
+import (
+	"fmt"
+	"io"
+	"os"
+	"strconv"
+
+	"github.com/aliyun/aliyun-oss-go-sdk/oss"
+)
+
+// PartCryptoContext save encryption or decryption information
+type PartCryptoContext struct {
+	ContentCipher ContentCipher
+	DataSize      int64
+	PartSize      int64
+}
+
+// Valid judge PartCryptoContext is valid or not
+func (pcc PartCryptoContext) Valid() bool {
+	if pcc.ContentCipher == nil || pcc.DataSize == 0 || pcc.PartSize == 0 {
+		return false
+	}
+	return true
+}
+
+// InitiateMultipartUpload initializes multipart upload for client encryption
+// cryptoContext.PartSize and cryptoContext.DataSize are input parameter
+// cryptoContext.PartSize must aligned to the secret iv length
+// cryptoContext.ContentCipher is output parameter
+// cryptoContext will be used in next API
+func (bucket CryptoBucket) InitiateMultipartUpload(objectKey string, cryptoContext *PartCryptoContext, options ...oss.Option) (oss.InitiateMultipartUploadResult, error) {
+	options = bucket.AddEncryptionUaSuffix(options)
+	var imur oss.InitiateMultipartUploadResult
+	if cryptoContext == nil {
+		return imur, fmt.Errorf("error,cryptoContext is nil")
+	}
+
+	if cryptoContext.PartSize <= 0 {
+		return imur, fmt.Errorf("invalid PartCryptoContext's PartSize %d", cryptoContext.PartSize)
+	}
+
+	cc, err := bucket.ContentCipherBuilder.ContentCipher()
+	if err != nil {
+		return imur, err
+	}
+
+	if cryptoContext.PartSize%int64(cc.GetAlignLen()) != 0 {
+		return imur, fmt.Errorf("PartCryptoContext's PartSize must be aligned to %d", cc.GetAlignLen())
+	}
+
+	opts := addCryptoHeaders(options, cc.GetCipherData())
+	if cryptoContext.DataSize > 0 {
+		opts = append(opts, oss.Meta(OssClientSideEncryptionDataSize, strconv.FormatInt(cryptoContext.DataSize, 10)))
+	}
+	opts = append(opts, oss.Meta(OssClientSideEncryptionPartSize, strconv.FormatInt(cryptoContext.PartSize, 10)))
+
+	imur, err = bucket.Bucket.InitiateMultipartUpload(objectKey, opts...)
+	if err == nil {
+		cryptoContext.ContentCipher = cc
+	}
+	return imur, err
+}
+
+// UploadPart uploads parts to oss, the part data are encrypted automaticly on client side
+// cryptoContext is the input parameter
+func (bucket CryptoBucket) UploadPart(imur oss.InitiateMultipartUploadResult, reader io.Reader,
+	partSize int64, partNumber int, cryptoContext PartCryptoContext, options ...oss.Option) (oss.UploadPart, error) {
+	options = bucket.AddEncryptionUaSuffix(options)
+	var uploadPart oss.UploadPart
+	if cryptoContext.ContentCipher == nil {
+		return uploadPart, fmt.Errorf("error,cryptoContext is nil or cryptoContext.ContentCipher is nil")
+	}
+
+	if partNumber < 1 {
+		return uploadPart, fmt.Errorf("partNumber:%d is smaller than 1", partNumber)
+	}
+
+	if cryptoContext.PartSize%int64(cryptoContext.ContentCipher.GetAlignLen()) != 0 {
+		return uploadPart, fmt.Errorf("PartCryptoContext's PartSize must be aligned to %d", cryptoContext.ContentCipher.GetAlignLen())
+	}
+
+	cipherData := cryptoContext.ContentCipher.GetCipherData().Clone()
+	// caclulate iv based on part number
+	if partNumber > 1 {
+		cipherData.SeekIV(uint64(partNumber-1) * uint64(cryptoContext.PartSize))
+	}
+
+	// for parallel upload part
+	partCC, _ := cryptoContext.ContentCipher.Clone(cipherData)
+
+	cryptoReader, err := partCC.EncryptContent(reader)
+	if err != nil {
+		return uploadPart, err
+	}
+
+	request := &oss.UploadPartRequest{
+		InitResult: &imur,
+		Reader:     cryptoReader,
+		PartSize:   partCC.GetEncryptedLen(partSize),
+		PartNumber: partNumber,
+	}
+
+	opts := addCryptoHeaders(options, partCC.GetCipherData())
+	if cryptoContext.DataSize > 0 {
+		opts = append(opts, oss.Meta(OssClientSideEncryptionDataSize, strconv.FormatInt(cryptoContext.DataSize, 10)))
+	}
+	opts = append(opts, oss.Meta(OssClientSideEncryptionPartSize, strconv.FormatInt(cryptoContext.PartSize, 10)))
+
+	result, err := bucket.Bucket.DoUploadPart(request, opts)
+	return result.Part, err
+}
+
+// UploadPartFromFile uploads part from the file, the part data are encrypted automaticly on client side
+// cryptoContext is the input parameter
+func (bucket CryptoBucket) UploadPartFromFile(imur oss.InitiateMultipartUploadResult, filePath string,
+	startPosition, partSize int64, partNumber int, cryptoContext PartCryptoContext, options ...oss.Option) (oss.UploadPart, error) {
+	options = bucket.AddEncryptionUaSuffix(options)
+	var uploadPart = oss.UploadPart{}
+	if cryptoContext.ContentCipher == nil {
+		return uploadPart, fmt.Errorf("error,cryptoContext is nil or cryptoContext.ContentCipher is nil")
+	}
+
+	if cryptoContext.PartSize%int64(cryptoContext.ContentCipher.GetAlignLen()) != 0 {
+		return uploadPart, fmt.Errorf("PartCryptoContext's PartSize must be aligned to %d", cryptoContext.ContentCipher.GetAlignLen())
+	}
+
+	fd, err := os.Open(filePath)
+	if err != nil {
+		return uploadPart, err
+	}
+	defer fd.Close()
+	fd.Seek(startPosition, os.SEEK_SET)
+
+	if partNumber < 1 {
+		return uploadPart, fmt.Errorf("partNumber:%d is smaller than 1", partNumber)
+	}
+
+	cipherData := cryptoContext.ContentCipher.GetCipherData().Clone()
+	// calculate iv based on part number
+	if partNumber > 1 {
+		cipherData.SeekIV(uint64(partNumber-1) * uint64(cryptoContext.PartSize))
+	}
+
+	// for parallel upload part
+	partCC, _ := cryptoContext.ContentCipher.Clone(cipherData)
+	cryptoReader, err := partCC.EncryptContent(fd)
+	if err != nil {
+		return uploadPart, err
+	}
+
+	encryptedLen := partCC.GetEncryptedLen(partSize)
+	opts := addCryptoHeaders(options, partCC.GetCipherData())
+	if cryptoContext.DataSize > 0 {
+		opts = append(opts, oss.Meta(OssClientSideEncryptionDataSize, strconv.FormatInt(cryptoContext.DataSize, 10)))
+	}
+	opts = append(opts, oss.Meta(OssClientSideEncryptionPartSize, strconv.FormatInt(cryptoContext.PartSize, 10)))
+
+	request := &oss.UploadPartRequest{
+		InitResult: &imur,
+		Reader:     cryptoReader,
+		PartSize:   encryptedLen,
+		PartNumber: partNumber,
+	}
+	result, err := bucket.Bucket.DoUploadPart(request, opts)
+	return result.Part, err
+}
+
+// UploadPartCopy uploads part copy
+func (bucket CryptoBucket) UploadPartCopy(imur oss.InitiateMultipartUploadResult, srcBucketName, srcObjectKey string,
+	startPosition, partSize int64, partNumber int, cryptoContext PartCryptoContext, options ...oss.Option) (oss.UploadPart, error) {
+	var uploadPart = oss.UploadPart{}
+	return uploadPart, fmt.Errorf("CryptoBucket doesn't support UploadPartCopy")
+}

+ 769 - 0
oss/crypto/crypto_multipart_test.go

@@ -0,0 +1,769 @@
+// multipart test
+
+package osscrypto
+
+import (
+	"io/ioutil"
+	"os"
+
+	"github.com/aliyun/aliyun-oss-go-sdk/oss"
+	. "gopkg.in/check.v1"
+)
+
+func (s *OssCryptoBucketSuite) TestMultipartUpload(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	fileName := "../../sample/BingWallpaper-2015-11-07.jpg"
+
+	srcMD5, err := GetFileMD5(fileName)
+	c.Assert(err, IsNil)
+
+	fileInfo, err := os.Stat(fileName)
+	dataSize := fileInfo.Size()
+	c.Assert(err, IsNil)
+
+	options := []oss.Option{oss.Meta("my", "myprop")}
+	var cryptoContext PartCryptoContext
+	cryptoContext.DataSize = dataSize
+	cryptoContext.PartSize = (dataSize / 16 / 3) * 16
+	imur, err := bucket.InitiateMultipartUpload(objectName, &cryptoContext, options...)
+	c.Assert(err, IsNil)
+
+	chunks, err := oss.SplitFileByPartSize(fileName, cryptoContext.PartSize)
+	c.Assert(err, IsNil)
+
+	fd, err := os.Open(fileName)
+	c.Assert(err, IsNil)
+	defer fd.Close()
+
+	var parts []oss.UploadPart
+	for _, chunk := range chunks {
+		fd.Seek(chunk.Offset, os.SEEK_SET)
+		part, err := bucket.UploadPart(imur, fd, chunk.Size, chunk.Number, cryptoContext)
+		c.Assert(err, IsNil)
+		parts = append(parts, part)
+	}
+
+	_, err = bucket.CompleteMultipartUpload(imur, parts)
+	c.Assert(err, IsNil)
+
+	meta, err := bucket.GetObjectDetailedMeta(objectName)
+	c.Assert(err, IsNil)
+
+	c.Assert(meta.Get("X-Oss-Meta-My"), Equals, "myprop")
+	c.Assert(meta.Get("X-Oss-Object-Type"), Equals, "Multipart")
+
+	downfileName := "test-go-sdk-file-" + RandLowStr(5)
+	err = bucket.GetObjectToFile(objectName, downfileName)
+	c.Assert(err, IsNil)
+
+	downFileMD5, err := GetFileMD5(downfileName)
+	c.Assert(err, IsNil)
+	c.Assert(downFileMD5, Equals, srcMD5)
+
+	os.Remove(downfileName)
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestMultipartUploadFromFile(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	fileName := "../../sample/BingWallpaper-2015-11-07.jpg"
+
+	srcMD5, err := GetFileMD5(fileName)
+	c.Assert(err, IsNil)
+
+	fileInfo, err := os.Stat(fileName)
+	dataSize := fileInfo.Size()
+	c.Assert(err, IsNil)
+
+	options := []oss.Option{oss.Meta("my", "myprop")}
+	var cryptoContext PartCryptoContext
+	cryptoContext.DataSize = dataSize
+	cryptoContext.PartSize = (dataSize / 16 / 3) * 16
+	imur, err := bucket.InitiateMultipartUpload(objectName, &cryptoContext, options...)
+	c.Assert(err, IsNil)
+
+	chunks, err := oss.SplitFileByPartSize(fileName, cryptoContext.PartSize)
+	c.Assert(err, IsNil)
+
+	var parts []oss.UploadPart
+	for _, chunk := range chunks {
+		part, err := bucket.UploadPartFromFile(imur, fileName, chunk.Offset, chunk.Size, chunk.Number, cryptoContext)
+		c.Assert(err, IsNil)
+		parts = append(parts, part)
+	}
+
+	_, err = bucket.CompleteMultipartUpload(imur, parts)
+	c.Assert(err, IsNil)
+
+	meta, err := bucket.GetObjectDetailedMeta(objectName)
+	c.Assert(err, IsNil)
+
+	c.Assert(meta.Get("X-Oss-Meta-My"), Equals, "myprop")
+	c.Assert(meta.Get("X-Oss-Object-Type"), Equals, "Multipart")
+
+	downfileName := "test-go-sdk-file-" + RandLowStr(5) + ".jpg"
+	err = bucket.GetObjectToFile(objectName, downfileName)
+	c.Assert(err, IsNil)
+
+	downFileMD5, err := GetFileMD5(downfileName)
+	c.Assert(err, IsNil)
+	c.Assert(downFileMD5, Equals, srcMD5)
+
+	os.Remove(downfileName)
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestMultipartUploadFromSmallSizeFile(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	fileName := "oss-go-sdk-test-file-" + RandStr(5)
+	fo, err := os.Create(fileName)
+	c.Assert(err, IsNil)
+	_, err = fo.Write([]byte("123"))
+	c.Assert(err, IsNil)
+	fo.Close()
+
+	srcMD5, err := GetFileMD5(fileName)
+	c.Assert(err, IsNil)
+
+	fileInfo, err := os.Stat(fileName)
+	dataSize := fileInfo.Size()
+	c.Assert(err, IsNil)
+
+	options := []oss.Option{oss.Meta("my", "myprop")}
+	var cryptoContext PartCryptoContext
+	cryptoContext.DataSize = dataSize
+	cryptoContext.PartSize = 16
+	imur, err := bucket.InitiateMultipartUpload(objectName, &cryptoContext, options...)
+	c.Assert(err, IsNil)
+
+	chunks, err := oss.SplitFileByPartSize(fileName, cryptoContext.PartSize)
+	c.Assert(err, IsNil)
+
+	var parts []oss.UploadPart
+	for _, chunk := range chunks {
+		part, err := bucket.UploadPartFromFile(imur, fileName, chunk.Offset, chunk.Size, chunk.Number, cryptoContext)
+		c.Assert(err, IsNil)
+		parts = append(parts, part)
+	}
+
+	_, err = bucket.CompleteMultipartUpload(imur, parts)
+	c.Assert(err, IsNil)
+
+	meta, err := bucket.GetObjectDetailedMeta(objectName)
+	c.Assert(err, IsNil)
+
+	c.Assert(meta.Get("X-Oss-Meta-My"), Equals, "myprop")
+	c.Assert(meta.Get("X-Oss-Object-Type"), Equals, "Multipart")
+
+	downfileName := "test-go-sdk-file-" + RandLowStr(5) + ".jpg"
+	err = bucket.GetObjectToFile(objectName, downfileName)
+	c.Assert(err, IsNil)
+
+	downFileMD5, err := GetFileMD5(downfileName)
+	c.Assert(err, IsNil)
+	c.Assert(downFileMD5, Equals, srcMD5)
+
+	os.Remove(fileName)
+	os.Remove(downfileName)
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestListUploadedPartsNormal(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	fileName := "../../sample/BingWallpaper-2015-11-07.jpg"
+
+	srcMD5, err := GetFileMD5(fileName)
+	c.Assert(err, IsNil)
+
+	fileInfo, err := os.Stat(fileName)
+	dataSize := fileInfo.Size()
+	c.Assert(err, IsNil)
+
+	// Upload
+	var cryptoContext PartCryptoContext
+	cryptoContext.DataSize = dataSize
+	cryptoContext.PartSize = (dataSize / 16 / 3) * 16
+	imurUpload, err := bucket.InitiateMultipartUpload(objectName, &cryptoContext)
+	chunks, err := oss.SplitFileByPartSize(fileName, cryptoContext.PartSize)
+	c.Assert(err, IsNil)
+
+	var partsUpload []oss.UploadPart
+	for _, chunk := range chunks {
+		part, err := bucket.UploadPartFromFile(imurUpload, fileName, chunk.Offset, chunk.Size, (int)(chunk.Number), cryptoContext)
+		c.Assert(err, IsNil)
+		partsUpload = append(partsUpload, part)
+	}
+
+	// List
+	lupr, err := bucket.ListUploadedParts(imurUpload)
+	c.Assert(err, IsNil)
+	c.Assert(len(lupr.UploadedParts), Equals, len(chunks))
+
+	// Complete
+	_, err = bucket.CompleteMultipartUpload(imurUpload, partsUpload)
+	c.Assert(err, IsNil)
+
+	// Download
+	downfileName := "test-go-sdk-file-" + RandLowStr(5) + ".jpg"
+	err = bucket.GetObjectToFile(objectName, downfileName)
+
+	downFileMD5, err := GetFileMD5(downfileName)
+	c.Assert(err, IsNil)
+	c.Assert(downFileMD5, Equals, srcMD5)
+
+	os.Remove(downfileName)
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestListUploadedPartsComplete(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	fileName := "../../sample/BingWallpaper-2015-11-07.jpg"
+
+	srcMD5, err := GetFileMD5(fileName)
+	c.Assert(err, IsNil)
+
+	fileInfo, err := os.Stat(fileName)
+	dataSize := fileInfo.Size()
+	c.Assert(err, IsNil)
+
+	// Upload
+	var cryptoContext PartCryptoContext
+	cryptoContext.DataSize = dataSize
+	cryptoContext.PartSize = (dataSize / 16 / 3) * 16
+	imurUpload, err := bucket.InitiateMultipartUpload(objectName, &cryptoContext)
+	chunks, err := oss.SplitFileByPartSize(fileName, cryptoContext.PartSize)
+	c.Assert(err, IsNil)
+
+	var partsUpload []oss.UploadPart
+	i := 0
+	// upload excepted the last part
+	for ; i < len(chunks)-1; i++ {
+		part, err := bucket.UploadPartFromFile(imurUpload, fileName, chunks[i].Offset, chunks[i].Size, (int)(chunks[i].Number), cryptoContext)
+		c.Assert(err, IsNil)
+		partsUpload = append(partsUpload, part)
+	}
+
+	// List
+	lupr, err := bucket.ListUploadedParts(imurUpload)
+	c.Assert(err, IsNil)
+	c.Assert(len(lupr.UploadedParts), Equals, len(chunks)-1)
+
+	lmur, err := bucket.ListMultipartUploads()
+	c.Assert(err, IsNil)
+	c.Assert(len(lmur.Uploads), Equals, 1)
+
+	// upload the last part with list part result
+	part, err := bucket.UploadPartFromFile(imurUpload, fileName, chunks[i].Offset, chunks[i].Size, (int)(chunks[i].Number), cryptoContext)
+	c.Assert(err, IsNil)
+	partsUpload = append(partsUpload, part)
+
+	// Complete
+	_, err = bucket.CompleteMultipartUpload(imurUpload, partsUpload)
+	c.Assert(err, IsNil)
+
+	// Download
+	downfileName := "test-go-sdk-file-" + RandLowStr(5) + ".jpg"
+	err = bucket.GetObjectToFile(objectName, downfileName)
+
+	downFileMD5, err := GetFileMD5(downfileName)
+	c.Assert(err, IsNil)
+	c.Assert(downFileMD5, Equals, srcMD5)
+
+	os.Remove(downfileName)
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestListUploadedPartsAbortUseInit(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	fileName := "../../sample/BingWallpaper-2015-11-07.jpg"
+
+	fileInfo, err := os.Stat(fileName)
+	dataSize := fileInfo.Size()
+	c.Assert(err, IsNil)
+
+	// Upload
+	var cryptoContext PartCryptoContext
+	cryptoContext.DataSize = dataSize
+	cryptoContext.PartSize = (dataSize / 16 / 3) * 16
+	imurUpload, err := bucket.InitiateMultipartUpload(objectName, &cryptoContext)
+	chunks, err := oss.SplitFileByPartSize(fileName, cryptoContext.PartSize)
+	c.Assert(err, IsNil)
+	c.Assert(cryptoContext.Valid(), Equals, true)
+
+	var partsUpload []oss.UploadPart
+	i := 0
+	// upload excepted the last part
+	for ; i < len(chunks)-1; i++ {
+		part, err := bucket.UploadPartFromFile(imurUpload, fileName, chunks[i].Offset, chunks[i].Size, (int)(chunks[i].Number), cryptoContext)
+		c.Assert(err, IsNil)
+		partsUpload = append(partsUpload, part)
+	}
+
+	// List
+	lupr, err := bucket.ListUploadedParts(imurUpload)
+	c.Assert(err, IsNil)
+	c.Assert(len(lupr.UploadedParts), Equals, len(chunks)-1)
+
+	lmur, err := bucket.ListMultipartUploads()
+	c.Assert(err, IsNil)
+	c.Assert(len(lmur.Uploads), Equals, 1)
+
+	// abort upload
+	err = bucket.AbortMultipartUpload(imurUpload)
+	c.Assert(err, IsNil)
+
+	// list again
+	lupr, err = bucket.ListUploadedParts(imurUpload)
+	c.Assert(err, NotNil)
+
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestListUploadedPartsAbortUseList(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	fileName := "../../sample/BingWallpaper-2015-11-07.jpg"
+
+	fileInfo, err := os.Stat(fileName)
+	dataSize := fileInfo.Size()
+	c.Assert(err, IsNil)
+
+	// Upload
+	var cryptoContext PartCryptoContext
+	cryptoContext.DataSize = dataSize
+	cryptoContext.PartSize = (dataSize / 16 / 3) * 16
+	imurUpload, err := bucket.InitiateMultipartUpload(objectName, &cryptoContext)
+	chunks, err := oss.SplitFileByPartSize(fileName, cryptoContext.PartSize)
+	c.Assert(err, IsNil)
+
+	var partsUpload []oss.UploadPart
+	i := 0
+	// upload excepted the last part
+	for ; i < len(chunks)-1; i++ {
+		part, err := bucket.UploadPartFromFile(imurUpload, fileName, chunks[i].Offset, chunks[i].Size, (int)(chunks[i].Number), cryptoContext)
+		c.Assert(err, IsNil)
+		partsUpload = append(partsUpload, part)
+	}
+
+	// List
+	lupr, err := bucket.ListUploadedParts(imurUpload)
+	c.Assert(err, IsNil)
+	c.Assert(len(lupr.UploadedParts), Equals, len(chunks)-1)
+
+	// abort upload
+	err = bucket.AbortMultipartUpload(imurUpload)
+	c.Assert(err, IsNil)
+
+	// list again
+	lupr, err = bucket.ListUploadedParts(imurUpload)
+	c.Assert(err, NotNil)
+
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestInitiateMultipartUpload(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	context := RandStr(ivSize * 1024 * 10)
+	fileName := "test-go-sdk-file-" + RandStr(5)
+
+	err = ioutil.WriteFile(fileName, []byte(context), 0666)
+	c.Assert(err, IsNil)
+
+	fileInfo, err := os.Stat(fileName)
+	dataSize := fileInfo.Size()
+	c.Assert(err, IsNil)
+
+	var cryptoContext PartCryptoContext
+	cryptoContext.DataSize = -1
+	cryptoContext.PartSize = (dataSize / 16 / 3) * 16
+	imurUpload, err := bucket.InitiateMultipartUpload(objectName, &cryptoContext)
+	c.Assert(err, IsNil)
+
+	cryptoContext.DataSize = dataSize
+	cryptoContext.PartSize = ivSize * 1024
+	imurUpload, err = bucket.InitiateMultipartUpload(objectName, &cryptoContext)
+	c.Assert(err, IsNil)
+
+	err = bucket.AbortMultipartUpload(imurUpload)
+	c.Assert(err, IsNil)
+
+	cryptoContext.DataSize = dataSize
+	cryptoContext.PartSize = ivSize / 2
+	imurUpload, err = bucket.InitiateMultipartUpload(objectName, &cryptoContext)
+	c.Assert(err, NotNil)
+
+	os.Remove(fileName)
+
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestUploadPartError(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	context := RandStr(ivSize * 1024 * 10)
+	fileName := "test-go-sdk-file-" + RandStr(5)
+
+	err = ioutil.WriteFile(fileName, []byte(context), 0666)
+	c.Assert(err, IsNil)
+
+	fileInfo, err := os.Stat(fileName)
+	dataSize := fileInfo.Size()
+	c.Assert(err, IsNil)
+
+	var cryptoContext PartCryptoContext
+	cryptoContext.DataSize = dataSize
+	cryptoContext.PartSize = ivSize * 1024
+	imur, err := bucket.InitiateMultipartUpload(objectName, &cryptoContext)
+	c.Assert(err, IsNil)
+
+	chunks, err := oss.SplitFileByPartSize(fileName, cryptoContext.PartSize)
+	c.Assert(err, IsNil)
+
+	fd, err := os.Open(fileName)
+	c.Assert(err, IsNil)
+
+	partCount := len(chunks)
+	for _, chunk := range chunks {
+		fd.Seek(chunk.Offset, os.SEEK_SET)
+		_, err := bucket.UploadPart(imur, fd, chunk.Size+1, chunk.Number, cryptoContext)
+		if chunk.Number < partCount {
+			c.Assert(err, IsNil)
+		} else {
+			c.Assert(err, NotNil)
+		}
+	}
+
+	for _, chunk := range chunks {
+		fd.Seek(chunk.Offset, os.SEEK_SET)
+		_, err := bucket.UploadPart(imur, fd, chunk.Size, 0, cryptoContext)
+		c.Assert(err, NotNil)
+	}
+	fd.Close()
+
+	err = bucket.AbortMultipartUpload(imur)
+	c.Assert(err, IsNil)
+	os.Remove(fileName)
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestUploadPartFromFileError(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	context := RandStr(ivSize * 1024 * 10)
+	fileName := "test-go-sdk-file-" + RandStr(5)
+
+	err = ioutil.WriteFile(fileName, []byte(context), 0666)
+	c.Assert(err, IsNil)
+
+	fileInfo, err := os.Stat(fileName)
+	dataSize := fileInfo.Size()
+	c.Assert(err, IsNil)
+
+	var cryptoContext PartCryptoContext
+	cryptoContext.DataSize = dataSize
+	cryptoContext.PartSize = ivSize * 1024
+	imur, err := bucket.InitiateMultipartUpload(objectName, &cryptoContext)
+	c.Assert(err, IsNil)
+
+	chunks, err := oss.SplitFileByPartSize(fileName, cryptoContext.PartSize)
+	c.Assert(err, IsNil)
+
+	for _, chunk := range chunks {
+		_, err := bucket.UploadPartFromFile(imur, fileName+".test", chunk.Offset, chunk.Size, chunk.Number, cryptoContext)
+		c.Assert(err, NotNil)
+	}
+
+	_, err = bucket.UploadPartFromFile(imur, fileName+".test", chunks[0].Offset, chunks[0].Size+1, chunks[0].Number, cryptoContext)
+	c.Assert(err, NotNil)
+
+	_, err = bucket.UploadPartFromFile(imur, fileName+".test", chunks[0].Offset, chunks[0].Size, 0, cryptoContext)
+	c.Assert(err, NotNil)
+
+	err = bucket.AbortMultipartUpload(imur)
+	c.Assert(err, IsNil)
+	os.Remove(fileName)
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestUploadPartCopyError(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	context := RandStr(ivSize * 1024 * 10)
+	fileName := "test-go-sdk-file-" + RandStr(5)
+
+	err = ioutil.WriteFile(fileName, []byte(context), 0666)
+	c.Assert(err, IsNil)
+
+	fileInfo, err := os.Stat(fileName)
+	dataSize := fileInfo.Size()
+	c.Assert(err, IsNil)
+
+	var cryptoContext PartCryptoContext
+	cryptoContext.DataSize = dataSize
+	cryptoContext.PartSize = ivSize * 1024
+	imur, err := bucket.InitiateMultipartUpload(objectName, &cryptoContext)
+	c.Assert(err, IsNil)
+
+	chunks, err := oss.SplitFileByPartSize(fileName, cryptoContext.PartSize)
+	c.Assert(err, IsNil)
+
+	_, err = bucket.UploadPartCopy(imur, bucketName, objectName, 0, chunks[0].Size, 1, cryptoContext)
+	c.Assert(err, NotNil)
+
+	err = bucket.AbortMultipartUpload(imur)
+	c.Assert(err, IsNil)
+	os.Remove(fileName)
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestMultipartUploadFromFileError(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	fileName := "../../sample/BingWallpaper-2015-11-07.jpg"
+
+	fileInfo, err := os.Stat(fileName)
+	dataSize := fileInfo.Size()
+	c.Assert(err, IsNil)
+
+	options := []oss.Option{oss.Meta("my", "myprop")}
+	var cryptoContext PartCryptoContext
+	cryptoContext.DataSize = dataSize
+	cryptoContext.PartSize = -1
+	_, err = bucket.InitiateMultipartUpload(objectName, nil, options...)
+	c.Assert(err, NotNil)
+
+	_, err = bucket.InitiateMultipartUpload(objectName, &cryptoContext, options...)
+	c.Assert(err, NotNil)
+
+	cryptoContext.PartSize = (dataSize / 16 / 3) * 16
+	imur, err := bucket.InitiateMultipartUpload(objectName, &cryptoContext, options...)
+	c.Assert(err, IsNil)
+
+	chunks, err := oss.SplitFileByPartSize(fileName, cryptoContext.PartSize)
+	c.Assert(err, IsNil)
+
+	bakCC := cryptoContext.ContentCipher
+	cryptoContext.ContentCipher = nil
+
+	i := 0
+	// upload excepted the last part
+	for ; i < len(chunks); i++ {
+		_, err = bucket.UploadPartFromFile(imur, fileName, chunks[i].Offset, chunks[i].Size, (int)(chunks[i].Number), cryptoContext)
+		c.Assert(err, NotNil)
+
+	}
+
+	i = 0
+	cryptoContext.ContentCipher = bakCC
+	cryptoContext.PartSize -= 1
+	for ; i < len(chunks); i++ {
+		_, err = bucket.UploadPartFromFile(imur, fileName, chunks[i].Offset, chunks[i].Size, (int)(chunks[i].Number), cryptoContext)
+		c.Assert(err, NotNil)
+	}
+
+	ForceDeleteBucket(client, bucketName, c)
+}
+
+func (s *OssCryptoBucketSuite) TestMultipartUploadPartError(c *C) {
+	// create a bucket with default proprety
+	client, err := oss.New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + RandLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// crypto bucket
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	contentProvider := CreateAesCtrCipher(masterRsaCipher)
+	bucket, err := GetCryptoBucket(client, bucketName, contentProvider)
+
+	objectName := objectNamePrefix + RandStr(8)
+	fileName := "../../sample/BingWallpaper-2015-11-07.jpg"
+
+	fileInfo, err := os.Stat(fileName)
+	dataSize := fileInfo.Size()
+	c.Assert(err, IsNil)
+
+	options := []oss.Option{oss.Meta("my", "myprop")}
+	var cryptoContext PartCryptoContext
+	cryptoContext.DataSize = dataSize
+	cryptoContext.PartSize = (dataSize / 16 / 3) * 16
+	imur, err := bucket.InitiateMultipartUpload(objectName, &cryptoContext, options...)
+	c.Assert(err, IsNil)
+
+	chunks, err := oss.SplitFileByPartSize(fileName, cryptoContext.PartSize)
+	c.Assert(err, IsNil)
+
+	bakCC := cryptoContext.ContentCipher
+	cryptoContext.ContentCipher = nil
+
+	fd, err := os.Open(fileName)
+	c.Assert(err, IsNil)
+
+	for _, chunk := range chunks {
+		fd.Seek(chunk.Offset, os.SEEK_SET)
+		_, err = bucket.UploadPart(imur, fd, chunk.Size, chunk.Number, cryptoContext)
+		c.Assert(err, NotNil)
+	}
+
+	cryptoContext.ContentCipher = bakCC
+	cryptoContext.PartSize -= 1
+	for _, chunk := range chunks {
+		_, err = bucket.UploadPart(imur, fd, chunk.Size, chunk.Number, cryptoContext)
+		c.Assert(err, NotNil)
+	}
+
+	ForceDeleteBucket(client, bucketName, c)
+}

+ 128 - 0
oss/crypto/crypto_type.go

@@ -0,0 +1,128 @@
+package osscrypto
+
+import (
+	"crypto/rand"
+	"encoding/binary"
+	"fmt"
+	"io"
+	math_rand "math/rand"
+	"time"
+)
+
+// MasterCipher encrypt or decrpt CipherData
+// support master key: rsa && ali kms
+type MasterCipher interface {
+	Encrypt([]byte) ([]byte, error)
+	Decrypt([]byte) ([]byte, error)
+	GetWrapAlgorithm() string
+	GetMatDesc() string
+}
+
+// ContentCipherBuilder is used to create ContentCipher for encryting object's data
+type ContentCipherBuilder interface {
+	ContentCipher() (ContentCipher, error)
+	ContentCipherEnv(Envelope) (ContentCipher, error)
+	GetMatDesc() string
+}
+
+// ContentCipher is used to encrypt or decrypt object's data
+type ContentCipher interface {
+	EncryptContent(io.Reader) (io.ReadCloser, error)
+	DecryptContent(io.Reader) (io.ReadCloser, error)
+	Clone(cd CipherData) (ContentCipher, error)
+	GetEncryptedLen(int64) int64
+	GetCipherData() *CipherData
+	GetAlignLen() int
+}
+
+// Envelope is stored in oss object's meta
+type Envelope struct {
+	IV                    string
+	CipherKey             string
+	MatDesc               string
+	WrapAlg               string
+	CEKAlg                string
+	UnencryptedMD5        string
+	UnencryptedContentLen string
+}
+
+func (el Envelope) IsValid() bool {
+	return len(el.IV) > 0 &&
+		len(el.CipherKey) > 0 &&
+		len(el.WrapAlg) > 0 &&
+		len(el.CEKAlg) > 0
+}
+
+func (el Envelope) String() string {
+	return fmt.Sprintf("IV=%s&CipherKey=%s&WrapAlg=%s&CEKAlg=%s", el.IV, el.CipherKey, el.WrapAlg, el.CEKAlg)
+}
+
+// CipherData is secret key information
+type CipherData struct {
+	IV            []byte
+	Key           []byte
+	MatDesc       string
+	WrapAlgorithm string
+	CEKAlgorithm  string
+	EncryptedIV   []byte
+	EncryptedKey  []byte
+}
+
+func (cd *CipherData) RandomKeyIv(keyLen int, ivLen int) error {
+	math_rand.Seed(time.Now().UnixNano())
+
+	// Key
+	cd.Key = make([]byte, keyLen)
+	if _, err := io.ReadFull(rand.Reader, cd.Key); err != nil {
+		return err
+	}
+
+	// sizeof uint64
+	if ivLen < 8 {
+		return fmt.Errorf("ivLen:%d less than 8", ivLen)
+	}
+
+	// IV:reserve 8 bytes
+	cd.IV = make([]byte, ivLen)
+	if _, err := io.ReadFull(rand.Reader, cd.IV[0:ivLen-8]); err != nil {
+		return err
+	}
+
+	// only use 4 byte,in order not to overflow when SeekIV()
+	randNumber := math_rand.Uint32()
+	cd.SetIV(uint64(randNumber))
+	return nil
+}
+
+func (cd *CipherData) SetIV(iv uint64) {
+	ivLen := len(cd.IV)
+	binary.BigEndian.PutUint64(cd.IV[ivLen-8:], iv)
+}
+
+func (cd *CipherData) GetIV() uint64 {
+	ivLen := len(cd.IV)
+	return binary.BigEndian.Uint64(cd.IV[ivLen-8:])
+}
+
+func (cd *CipherData) SeekIV(startPos uint64) {
+	cd.SetIV(cd.GetIV() + startPos/uint64(len(cd.IV)))
+}
+
+func (cd *CipherData) Clone() CipherData {
+	var cloneCd CipherData
+	cloneCd = *cd
+
+	cloneCd.Key = make([]byte, len(cd.Key))
+	copy(cloneCd.Key, cd.Key)
+
+	cloneCd.IV = make([]byte, len(cd.IV))
+	copy(cloneCd.IV, cd.IV)
+
+	cloneCd.EncryptedIV = make([]byte, len(cd.EncryptedIV))
+	copy(cloneCd.EncryptedIV, cd.EncryptedIV)
+
+	cloneCd.EncryptedKey = make([]byte, len(cd.EncryptedKey))
+	copy(cloneCd.EncryptedKey, cd.EncryptedKey)
+
+	return cloneCd
+}

+ 12 - 0
oss/crypto/crypto_upload.go

@@ -0,0 +1,12 @@
+package osscrypto
+
+import (
+	"fmt"
+
+	"github.com/aliyun/aliyun-oss-go-sdk/oss"
+)
+
+// UploadFile with multi part mode
+func (bucket CryptoBucket) UploadFile(objectKey, filePath string, partSize int64, options ...oss.Option) error {
+	return fmt.Errorf("CryptoBucket doesn't support UploadFile")
+}

+ 85 - 0
oss/crypto/master_alikms_cipher.go

@@ -0,0 +1,85 @@
+package osscrypto
+
+import (
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+
+	kms "github.com/aliyun/alibaba-cloud-sdk-go/services/kms"
+)
+
+// CreateMasterAliKms Create master key interface implemented by ali kms
+// matDesc will be converted to json string
+func CreateMasterAliKms(matDesc map[string]string, kmsID string, kmsClient *kms.Client) (MasterCipher, error) {
+	var masterCipher MasterAliKmsCipher
+	if kmsID == "" || kmsClient == nil {
+		return masterCipher, fmt.Errorf("kmsID is empty or kmsClient is nil")
+	}
+
+	var jsonDesc string
+	if len(matDesc) > 0 {
+		b, err := json.Marshal(matDesc)
+		if err != nil {
+			return masterCipher, err
+		}
+		jsonDesc = string(b)
+	}
+
+	masterCipher.MatDesc = jsonDesc
+	masterCipher.KmsID = kmsID
+	masterCipher.KmsClient = kmsClient
+	return masterCipher, nil
+}
+
+// MasterAliKmsCipher ali kms master key interface
+type MasterAliKmsCipher struct {
+	MatDesc   string
+	KmsID     string
+	KmsClient *kms.Client
+}
+
+// GetWrapAlgorithm get master key wrap algorithm
+func (mrc MasterAliKmsCipher) GetWrapAlgorithm() string {
+	return KmsAliCryptoWrap
+}
+
+// GetMatDesc get master key describe
+func (mkms MasterAliKmsCipher) GetMatDesc() string {
+	return mkms.MatDesc
+}
+
+// Encrypt  encrypt data by ali kms
+// Mainly used to encrypt object's symmetric secret key and iv
+func (mkms MasterAliKmsCipher) Encrypt(plainData []byte) ([]byte, error) {
+	// kms Plaintext must be base64 encoded
+	base64Plain := base64.StdEncoding.EncodeToString(plainData)
+	request := kms.CreateEncryptRequest()
+	request.RpcRequest.Scheme = "https"
+	request.RpcRequest.Method = "POST"
+	request.RpcRequest.AcceptFormat = "json"
+
+	request.KeyId = mkms.KmsID
+	request.Plaintext = base64Plain
+
+	response, err := mkms.KmsClient.Encrypt(request)
+	if err != nil {
+		return nil, err
+	}
+	return base64.StdEncoding.DecodeString(response.CiphertextBlob)
+}
+
+// Decrypt decrypt data by ali kms
+// Mainly used to decrypt object's symmetric secret key and iv
+func (mkms MasterAliKmsCipher) Decrypt(cryptoData []byte) ([]byte, error) {
+	base64Crypto := base64.StdEncoding.EncodeToString(cryptoData)
+	request := kms.CreateDecryptRequest()
+	request.RpcRequest.Scheme = "https"
+	request.RpcRequest.Method = "POST"
+	request.RpcRequest.AcceptFormat = "json"
+	request.CiphertextBlob = string(base64Crypto)
+	response, err := mkms.KmsClient.Decrypt(request)
+	if err != nil {
+		return nil, err
+	}
+	return base64.StdEncoding.DecodeString(response.Plaintext)
+}

+ 89 - 0
oss/crypto/master_alikms_cipher_test.go

@@ -0,0 +1,89 @@
+package osscrypto
+
+import (
+	crypto_rand "crypto/rand"
+	"encoding/base64"
+	"io"
+	"math/rand"
+	"time"
+
+	kms "github.com/aliyun/alibaba-cloud-sdk-go/services/kms"
+	. "gopkg.in/check.v1"
+)
+
+func (s *OssCryptoBucketSuite) TestKmsClient(c *C) {
+	rand.Seed(time.Now().UnixNano())
+	kmsClient, err := kms.NewClientWithAccessKey(kmsRegion, kmsAccessID, kmsAccessKey)
+	c.Assert(err, IsNil)
+
+	// encrypte
+	enReq := kms.CreateEncryptRequest()
+	enReq.RpcRequest.Scheme = "https"
+	enReq.RpcRequest.Method = "POST"
+	enReq.RpcRequest.AcceptFormat = "json"
+
+	enReq.KeyId = kmsID
+
+	buff := make([]byte, 10)
+	_, err = io.ReadFull(crypto_rand.Reader, buff)
+	c.Assert(err, IsNil)
+	enReq.Plaintext = base64.StdEncoding.EncodeToString(buff)
+
+	enResponse, err := kmsClient.Encrypt(enReq)
+	c.Assert(err, IsNil)
+
+	// decrypte
+	deReq := kms.CreateDecryptRequest()
+	deReq.RpcRequest.Scheme = "https"
+	deReq.RpcRequest.Method = "POST"
+	deReq.RpcRequest.AcceptFormat = "json"
+	deReq.CiphertextBlob = enResponse.CiphertextBlob
+	deResponse, err := kmsClient.Decrypt(deReq)
+	c.Assert(err, IsNil)
+	c.Assert(deResponse.Plaintext, Equals, enReq.Plaintext)
+}
+
+func (s *OssCryptoBucketSuite) TestMasterAliKmsCipherSuccess(c *C) {
+
+	kmsClient, err := kms.NewClientWithAccessKey(kmsRegion, kmsAccessID, kmsAccessKey)
+	c.Assert(err, IsNil)
+
+	masterCipher, _ := CreateMasterAliKms(matDesc, kmsID, kmsClient)
+
+	var cd CipherData
+	err = cd.RandomKeyIv(aesKeySize, ivSize)
+	c.Assert(err, IsNil)
+
+	cd.WrapAlgorithm = masterCipher.GetWrapAlgorithm()
+	cd.CEKAlgorithm = KmsAliCryptoWrap
+	cd.MatDesc = masterCipher.GetMatDesc()
+
+	// EncryptedKey
+	cd.EncryptedKey, err = masterCipher.Encrypt(cd.Key)
+
+	// EncryptedIV
+	cd.EncryptedIV, err = masterCipher.Encrypt(cd.IV)
+
+	cloneData := cd.Clone()
+
+	cloneData.Key, _ = masterCipher.Decrypt(cloneData.EncryptedKey)
+	cloneData.IV, _ = masterCipher.Decrypt(cloneData.EncryptedIV)
+
+	c.Assert(string(cd.Key), Equals, string(cloneData.Key))
+	c.Assert(string(cd.IV), Equals, string(cloneData.IV))
+
+}
+
+func (s *OssCryptoBucketSuite) TestMasterAliKmsCipherError(c *C) {
+	kmsClient, err := kms.NewClientWithAccessKey(kmsRegion, kmsAccessID, kmsAccessKey)
+	c.Assert(err, IsNil)
+
+	masterCipher, _ := CreateMasterAliKms(matDesc, kmsID, kmsClient)
+	v := masterCipher.(MasterAliKmsCipher)
+	v.KmsID = ""
+	_, err = v.Encrypt([]byte("hellow"))
+	c.Assert(err, NotNil)
+
+	_, err = v.Decrypt([]byte("hellow"))
+	c.Assert(err, NotNil)
+}

+ 102 - 0
oss/crypto/master_rsa_cipher.go

@@ -0,0 +1,102 @@
+package osscrypto
+
+import (
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/x509"
+	"encoding/asn1"
+	"encoding/json"
+	"encoding/pem"
+	"fmt"
+)
+
+// CreateMasterRsa Create master key interface implemented by rsa
+// matDesc will be converted to json string
+func CreateMasterRsa(matDesc map[string]string, publicKey string, privateKey string) (MasterCipher, error) {
+	var masterCipher MasterRsaCipher
+	var jsonDesc string
+	if len(matDesc) > 0 {
+		b, err := json.Marshal(matDesc)
+		if err != nil {
+			return masterCipher, err
+		}
+		jsonDesc = string(b)
+	}
+	masterCipher.MatDesc = jsonDesc
+	masterCipher.PublicKey = publicKey
+	masterCipher.PrivateKey = privateKey
+	return masterCipher, nil
+}
+
+// MasterRsaCipher rsa master key interface
+type MasterRsaCipher struct {
+	MatDesc    string
+	PublicKey  string
+	PrivateKey string
+}
+
+// GetWrapAlgorithm get master key wrap algorithm
+func (mrc MasterRsaCipher) GetWrapAlgorithm() string {
+	return RsaCryptoWrap
+}
+
+// GetMatDesc get master key describe
+func (mrc MasterRsaCipher) GetMatDesc() string {
+	return mrc.MatDesc
+}
+
+// Encrypt encrypt data by rsa public key
+// Mainly used to encrypt object's symmetric secret key and iv
+func (mrc MasterRsaCipher) Encrypt(plainData []byte) ([]byte, error) {
+	block, _ := pem.Decode([]byte(mrc.PublicKey))
+	if block == nil {
+		return nil, fmt.Errorf("pem.Decode public key error")
+	}
+
+	var pub *rsa.PublicKey
+	if block.Type == "PUBLIC KEY" {
+		// pks8 format
+		pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
+		if err != nil {
+			return nil, err
+		}
+		pub = pubInterface.(*rsa.PublicKey)
+	} else if block.Type == "RSA PUBLIC KEY" {
+		// pks1 format
+		pub = &rsa.PublicKey{}
+		_, err := asn1.Unmarshal(block.Bytes, pub)
+		if err != nil {
+			return nil, err
+		}
+	} else {
+		return nil, fmt.Errorf("not supported public key,type:%s", block.Type)
+	}
+	return rsa.EncryptPKCS1v15(rand.Reader, pub, plainData)
+}
+
+// Decrypt Decrypt data by rsa private key
+// Mainly used to decrypt object's symmetric secret key and iv
+func (mrc MasterRsaCipher) Decrypt(cryptoData []byte) ([]byte, error) {
+	block, _ := pem.Decode([]byte(mrc.PrivateKey))
+	if block == nil {
+		return nil, fmt.Errorf("pem.Decode private key error")
+	}
+
+	if block.Type == "PRIVATE KEY" {
+		// pks8 format
+		privInterface, err := x509.ParsePKCS8PrivateKey(block.Bytes)
+		if err != nil {
+			return nil, err
+		}
+		return rsa.DecryptPKCS1v15(rand.Reader, privInterface.(*rsa.PrivateKey), cryptoData)
+	} else if block.Type == "RSA PRIVATE KEY" {
+		// pks1 format
+		priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
+		if err != nil {
+			return nil, err
+		}
+		return rsa.DecryptPKCS1v15(rand.Reader, priv, cryptoData)
+	} else {
+		return nil, fmt.Errorf("not supported private key,type:%s", block.Type)
+	}
+}

+ 41 - 0
oss/crypto/master_rsa_cipher_test.go

@@ -0,0 +1,41 @@
+package osscrypto
+
+import (
+	"strings"
+
+	. "gopkg.in/check.v1"
+)
+
+func (s *OssCryptoBucketSuite) TestMasterRsaError(c *C) {
+
+	masterRsaCipher, _ := CreateMasterRsa(matDesc, RandLowStr(100), rsaPrivateKey)
+	_, err := masterRsaCipher.Encrypt([]byte("123"))
+	c.Assert(err, NotNil)
+
+	masterRsaCipher, _ = CreateMasterRsa(matDesc, rsaPublicKey, RandLowStr(100))
+	_, err = masterRsaCipher.Decrypt([]byte("123"))
+	c.Assert(err, NotNil)
+
+	testPrivateKey := rsaPrivateKey
+	[]byte(testPrivateKey)[100] = testPrivateKey[90]
+	masterRsaCipher, _ = CreateMasterRsa(matDesc, rsaPublicKey, testPrivateKey)
+	_, err = masterRsaCipher.Decrypt([]byte("123"))
+	c.Assert(err, NotNil)
+
+	masterRsaCipher, _ = CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+
+	var cipherData CipherData
+	err = cipherData.RandomKeyIv(aesKeySize/2, ivSize/4)
+	c.Assert(err, NotNil)
+
+	masterRsaCipher, _ = CreateMasterRsa(matDesc, rsaPublicKey, rsaPrivateKey)
+	v := masterRsaCipher.(MasterRsaCipher)
+
+	v.PublicKey = strings.Replace(rsaPublicKey, "PUBLIC KEY", "CERTIFICATE", -1)
+	_, err = v.Encrypt([]byte("HELLOW"))
+	c.Assert(err, NotNil)
+
+	v.PrivateKey = strings.Replace(rsaPrivateKey, "PRIVATE KEY", "CERTIFICATE", -1)
+	_, err = v.Decrypt([]byte("HELLOW"))
+	c.Assert(err, NotNil)
+}

+ 25 - 36
oss/download.go

@@ -30,7 +30,7 @@ func (bucket Bucket) DownloadFile(objectKey, filePath string, partSize int64, op
 		return errors.New("oss: part size smaller than 1")
 	}
 
-	uRange, err := getRangeConfig(options)
+	uRange, err := GetRangeConfig(options)
 	if err != nil {
 		return err
 	}
@@ -39,7 +39,7 @@ func (bucket Bucket) DownloadFile(objectKey, filePath string, partSize int64, op
 	routines := getRoutines(options)
 
 	var strVersionId string
-	versionId, _ := findOption(options, "versionId", nil)
+	versionId, _ := FindOption(options, "versionId", nil)
 	if versionId != nil {
 		strVersionId = versionId.(string)
 	}
@@ -64,17 +64,6 @@ func getDownloadCpFilePath(cpConf *cpConfig, srcBucket, srcObject, versionId, de
 	return cpConf.FilePath
 }
 
-// getRangeConfig gets the download range from the options.
-func getRangeConfig(options []Option) (*unpackedRange, error) {
-	rangeOpt, err := findOption(options, HTTPHeaderRange, nil)
-	if err != nil || rangeOpt == nil {
-		return nil, err
-	}
-	return parseRange(rangeOpt.(string))
-}
-
-// ----- concurrent download without checkpoint  -----
-
 // downloadWorkerArg is download worker's parameters
 type downloadWorkerArg struct {
 	bucket    *Bucket
@@ -127,7 +116,7 @@ func downloadWorker(id int, arg downloadWorkerArg, jobs <-chan downloadPart, res
 
 		var crcCalc hash.Hash64
 		if arg.enableCRC {
-			crcCalc = crc64.New(crcTable())
+			crcCalc = crc64.New(CrcTable())
 			contentLen := part.End - part.Start + 1
 			rd = ioutil.NopCloser(TeeReader(rd, crcCalc, contentLen, nil, nil))
 		}
@@ -186,11 +175,11 @@ type downloadPart struct {
 }
 
 // getDownloadParts gets download parts
-func getDownloadParts(objectSize, partSize int64, uRange *unpackedRange) []downloadPart {
+func getDownloadParts(objectSize, partSize int64, uRange *UnpackedRange) []downloadPart {
 	parts := []downloadPart{}
 	part := downloadPart{}
 	i := 0
-	start, end := adjustRange(uRange, objectSize)
+	start, end := AdjustRange(uRange, objectSize)
 	for offset := start; offset < end; offset += partSize {
 		part.Index = i
 		part.Start = offset
@@ -227,9 +216,9 @@ func combineCRCInParts(dps []downloadPart) uint64 {
 }
 
 // downloadFile downloads file concurrently without checkpoint.
-func (bucket Bucket) downloadFile(objectKey, filePath string, partSize int64, options []Option, routines int, uRange *unpackedRange) error {
+func (bucket Bucket) downloadFile(objectKey, filePath string, partSize int64, options []Option, routines int, uRange *UnpackedRange) error {
 	tempFilePath := filePath + TempFileSuffix
-	listener := getProgressListener(options)
+	listener := GetProgressListener(options)
 
 	// If the file does not exist, create one. If exists, the download will overwrite it.
 	fd, err := os.OpenFile(tempFilePath, os.O_WRONLY|os.O_CREATE, FilePermMode)
@@ -240,23 +229,23 @@ func (bucket Bucket) downloadFile(objectKey, filePath string, partSize int64, op
 
 	// Get the object detailed meta for object whole size
 	// must delete header:range to get whole object size
-	skipOptions := deleteOption(options, HTTPHeaderRange)
+	skipOptions := DeleteOption(options, HTTPHeaderRange)
 	meta, err := bucket.GetObjectDetailedMeta(objectKey, skipOptions...)
 	if err != nil {
 		return err
 	}
 
-	objectSize, err := strconv.ParseInt(meta.Get(HTTPHeaderContentLength), 10, 0)
+	objectSize, err := strconv.ParseInt(meta.Get(HTTPHeaderContentLength), 10, 64)
 	if err != nil {
 		return err
 	}
 
 	enableCRC := false
 	expectedCRC := (uint64)(0)
-	if bucket.getConfig().IsEnableCRC && meta.Get(HTTPHeaderOssCRC64) != "" {
-		if uRange == nil || (!uRange.hasStart && !uRange.hasEnd) {
+	if bucket.GetConfig().IsEnableCRC && meta.Get(HTTPHeaderOssCRC64) != "" {
+		if uRange == nil || (!uRange.HasStart && !uRange.HasEnd) {
 			enableCRC = true
-			expectedCRC, _ = strconv.ParseUint(meta.Get(HTTPHeaderOssCRC64), 10, 0)
+			expectedCRC, _ = strconv.ParseUint(meta.Get(HTTPHeaderOssCRC64), 10, 64)
 		}
 	}
 
@@ -309,7 +298,7 @@ func (bucket Bucket) downloadFile(objectKey, filePath string, partSize int64, op
 
 	if enableCRC {
 		actualCRC := combineCRCInParts(parts)
-		err = checkDownloadCRC(actualCRC, expectedCRC)
+		err = CheckDownloadCRC(actualCRC, expectedCRC)
 		if err != nil {
 			return err
 		}
@@ -343,7 +332,7 @@ type objectStat struct {
 }
 
 // isValid flags of checkpoint data is valid. It returns true when the data is valid and the checkpoint is valid and the object is not updated.
-func (cp downloadCheckpoint) isValid(meta http.Header, uRange *unpackedRange) (bool, error) {
+func (cp downloadCheckpoint) isValid(meta http.Header, uRange *UnpackedRange) (bool, error) {
 	// Compare the CP's Magic and the MD5
 	cpb := cp
 	cpb.MD5 = ""
@@ -355,7 +344,7 @@ func (cp downloadCheckpoint) isValid(meta http.Header, uRange *unpackedRange) (b
 		return false, nil
 	}
 
-	objectSize, err := strconv.ParseInt(meta.Get(HTTPHeaderContentLength), 10, 0)
+	objectSize, err := strconv.ParseInt(meta.Get(HTTPHeaderContentLength), 10, 64)
 	if err != nil {
 		return false, err
 	}
@@ -369,7 +358,7 @@ func (cp downloadCheckpoint) isValid(meta http.Header, uRange *unpackedRange) (b
 
 	// Check the download range
 	if uRange != nil {
-		start, end := adjustRange(uRange, objectSize)
+		start, end := AdjustRange(uRange, objectSize)
 		if start != cp.Start || end != cp.End {
 			return false, nil
 		}
@@ -436,13 +425,13 @@ func (cp downloadCheckpoint) getCompletedBytes() int64 {
 }
 
 // prepare initiates download tasks
-func (cp *downloadCheckpoint) prepare(meta http.Header, bucket *Bucket, objectKey, filePath string, partSize int64, uRange *unpackedRange) error {
+func (cp *downloadCheckpoint) prepare(meta http.Header, bucket *Bucket, objectKey, filePath string, partSize int64, uRange *UnpackedRange) error {
 	// CP
 	cp.Magic = downloadCpMagic
 	cp.FilePath = filePath
 	cp.Object = objectKey
 
-	objectSize, err := strconv.ParseInt(meta.Get(HTTPHeaderContentLength), 10, 0)
+	objectSize, err := strconv.ParseInt(meta.Get(HTTPHeaderContentLength), 10, 64)
 	if err != nil {
 		return err
 	}
@@ -451,10 +440,10 @@ func (cp *downloadCheckpoint) prepare(meta http.Header, bucket *Bucket, objectKe
 	cp.ObjStat.LastModified = meta.Get(HTTPHeaderLastModified)
 	cp.ObjStat.Etag = meta.Get(HTTPHeaderEtag)
 
-	if bucket.getConfig().IsEnableCRC && meta.Get(HTTPHeaderOssCRC64) != "" {
-		if uRange == nil || (!uRange.hasStart && !uRange.hasEnd) {
+	if bucket.GetConfig().IsEnableCRC && meta.Get(HTTPHeaderOssCRC64) != "" {
+		if uRange == nil || (!uRange.HasStart && !uRange.HasEnd) {
 			cp.enableCRC = true
-			cp.CRC, _ = strconv.ParseUint(meta.Get(HTTPHeaderOssCRC64), 10, 0)
+			cp.CRC, _ = strconv.ParseUint(meta.Get(HTTPHeaderOssCRC64), 10, 64)
 		}
 	}
 
@@ -474,9 +463,9 @@ func (cp *downloadCheckpoint) complete(cpFilePath, downFilepath string) error {
 }
 
 // downloadFileWithCp downloads files with checkpoint.
-func (bucket Bucket) downloadFileWithCp(objectKey, filePath string, partSize int64, options []Option, cpFilePath string, routines int, uRange *unpackedRange) error {
+func (bucket Bucket) downloadFileWithCp(objectKey, filePath string, partSize int64, options []Option, cpFilePath string, routines int, uRange *UnpackedRange) error {
 	tempFilePath := filePath + TempFileSuffix
-	listener := getProgressListener(options)
+	listener := GetProgressListener(options)
 
 	// Load checkpoint data.
 	dcp := downloadCheckpoint{}
@@ -487,7 +476,7 @@ func (bucket Bucket) downloadFileWithCp(objectKey, filePath string, partSize int
 
 	// Get the object detailed meta for object whole size
 	// must delete header:range to get whole object size
-	skipOptions := deleteOption(options, HTTPHeaderRange)
+	skipOptions := DeleteOption(options, HTTPHeaderRange)
 	meta, err := bucket.GetObjectDetailedMeta(objectKey, skipOptions...)
 	if err != nil {
 		return err
@@ -559,7 +548,7 @@ func (bucket Bucket) downloadFileWithCp(objectKey, filePath string, partSize int
 
 	if dcp.enableCRC {
 		actualCRC := combineCRCInParts(dcp.Parts)
-		err = checkDownloadCRC(actualCRC, dcp.CRC)
+		err = CheckDownloadCRC(actualCRC, dcp.CRC)
 		if err != nil {
 			return err
 		}

+ 45 - 45
oss/download_test.go

@@ -93,9 +93,9 @@ func (s *OssDownloadSuite) TearDownTest(c *C) {
 
 // TestDownloadRoutineWithoutRecovery multipart downloads without checkpoint
 func (s *OssDownloadSuite) TestDownloadRoutineWithoutRecovery(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
-	newFile := randStr(8) + ".jpg"
+	newFile := RandStr(8) + ".jpg"
 
 	// Upload a file
 	err := s.bucket.UploadFile(objectName, fileName, 100*1024, Routines(3))
@@ -155,9 +155,9 @@ func DownErrorHooker(part downloadPart) error {
 
 // TestDownloadRoutineWithRecovery multi-routine resumable download
 func (s *OssDownloadSuite) TestDownloadRoutineWithRecovery(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
-	newFile := randStr(8) + ".jpg"
+	newFile := RandStr(8) + ".jpg"
 
 	// Upload a file
 	err := s.bucket.UploadFile(objectName, fileName, 100*1024, Routines(3))
@@ -269,9 +269,9 @@ func (s *OssDownloadSuite) TestDownloadRoutineWithRecovery(c *C) {
 
 // TestDownloadOption options
 func (s *OssDownloadSuite) TestDownloadOption(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
-	newFile := randStr(8) + ".jpg"
+	newFile := RandStr(8) + ".jpg"
 
 	// Upload the file
 	err := s.bucket.UploadFile(objectName, fileName, 100*1024, Routines(3))
@@ -309,9 +309,9 @@ func (s *OssDownloadSuite) TestDownloadOption(c *C) {
 
 // TestDownloadObjectChange tests the file is updated during the upload
 func (s *OssDownloadSuite) TestDownloadObjectChange(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
-	newFile := randStr(8) + ".jpg"
+	newFile := RandStr(8) + ".jpg"
 
 	// Upload a file
 	err := s.bucket.UploadFile(objectName, fileName, 100*1024, Routines(3))
@@ -337,9 +337,9 @@ func (s *OssDownloadSuite) TestDownloadObjectChange(c *C) {
 
 // TestDownloadNegative tests downloading negative
 func (s *OssDownloadSuite) TestDownloadNegative(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
-	newFile := randStr(8) + ".jpg"
+	newFile := RandStr(8) + ".jpg"
 
 	// Upload a file
 	err := s.bucket.UploadFile(objectName, fileName, 100*1024, Routines(3))
@@ -389,10 +389,10 @@ func (s *OssDownloadSuite) TestDownloadNegative(c *C) {
 
 // TestDownloadWithRange tests concurrent downloading with range specified and checkpoint enabled
 func (s *OssDownloadSuite) TestDownloadWithRange(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
-	newFile := randStr(8) + ".jpg"
-	newFileGet := randStr(8) + "-.jpg"
+	newFile := RandStr(8) + ".jpg"
+	newFileGet := RandStr(8) + "-.jpg"
 
 	// Upload a file
 	err := s.bucket.UploadFile(objectName, fileName, 100*1024, Routines(3))
@@ -483,10 +483,10 @@ func (s *OssDownloadSuite) TestDownloadWithRange(c *C) {
 
 // TestDownloadWithCheckoutAndRange tests concurrent downloading with range specified and checkpoint enabled
 func (s *OssDownloadSuite) TestDownloadWithCheckoutAndRange(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
-	newFile := randStr(8) + ".jpg"
-	newFileGet := randStr(8) + "-get.jpg"
+	newFile := RandStr(8) + ".jpg"
+	newFileGet := RandStr(8) + "-get.jpg"
 
 	// Upload a file
 	err := s.bucket.UploadFile(objectName, fileName, 100*1024, Routines(3), Checkpoint(true, fileName+".cp"))
@@ -681,7 +681,7 @@ func (s *OssDownloadSuite) TestVersioningDownloadWithoutCheckPoint(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(6)
+	bucketName := bucketNamePrefix + RandLowStr(6)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 
@@ -694,13 +694,13 @@ func (s *OssDownloadSuite) TestVersioningDownloadWithoutCheckPoint(c *C) {
 	c.Assert(err, IsNil)
 
 	// begin test
-	objectName := objectNamePrefix + randStr(8)
-	fileName := "test-file-" + randStr(8)
-	fileData := randStr(500 * 1024)
-	createFile(fileName, fileData, c)
+	objectName := objectNamePrefix + RandStr(8)
+	fileName := "test-file-" + RandStr(8)
+	fileData := RandStr(500 * 1024)
+	CreateFile(fileName, fileData, c)
 
-	newFile := randStr(8) + ".jpg"
-	newFileGet := randStr(8) + "-.jpg"
+	newFile := RandStr(8) + ".jpg"
+	newFileGet := RandStr(8) + "-.jpg"
 
 	// Upload a file
 	var respHeader http.Header
@@ -816,7 +816,7 @@ func (s *OssDownloadSuite) TestVersioningDownloadWithoutCheckPoint(c *C) {
 	os.Remove(newFileGet)
 	err = bucket.DeleteObject(objectName)
 	c.Assert(err, IsNil)
-	forceDeleteBucket(client, bucketName, c)
+	ForceDeleteBucket(client, bucketName, c)
 }
 
 func (s *OssDownloadSuite) TestVersioningDownloadWithCheckPoint(c *C) {
@@ -824,7 +824,7 @@ func (s *OssDownloadSuite) TestVersioningDownloadWithCheckPoint(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(6)
+	bucketName := bucketNamePrefix + RandLowStr(6)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 
@@ -837,11 +837,11 @@ func (s *OssDownloadSuite) TestVersioningDownloadWithCheckPoint(c *C) {
 	c.Assert(err, IsNil)
 
 	// begin test
-	objectName := objectNamePrefix + randStr(8)
-	fileName := "test-file-" + randStr(8)
-	fileData := randStr(500 * 1024)
-	createFile(fileName, fileData, c)
-	newFile := randStr(8) + ".jpg"
+	objectName := objectNamePrefix + RandStr(8)
+	fileName := "test-file-" + RandStr(8)
+	fileData := RandStr(500 * 1024)
+	CreateFile(fileName, fileData, c)
+	newFile := RandStr(8) + ".jpg"
 
 	// Upload a file
 	var respHeader http.Header
@@ -878,7 +878,7 @@ func (s *OssDownloadSuite) TestVersioningDownloadWithCheckPoint(c *C) {
 	os.Remove(newFile)
 	err = bucket.DeleteObject(objectName)
 	c.Assert(err, IsNil)
-	forceDeleteBucket(client, bucketName, c)
+	ForceDeleteBucket(client, bucketName, c)
 }
 
 func (s *OssDownloadSuite) TestdownloadFileChoiceOptions(c *C) {
@@ -886,18 +886,18 @@ func (s *OssDownloadSuite) TestdownloadFileChoiceOptions(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(6)
+	bucketName := bucketNamePrefix + RandLowStr(6)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 
 	bucket, err := client.Bucket(bucketName)
 
 	// begin test
-	objectName := objectNamePrefix + randStr(8)
-	fileName := "test-file-" + randStr(8)
-	fileData := randStr(500 * 1024)
-	createFile(fileName, fileData, c)
-	newFile := randStr(8) + ".jpg"
+	objectName := objectNamePrefix + RandStr(8)
+	fileName := "test-file-" + RandStr(8)
+	fileData := RandStr(500 * 1024)
+	CreateFile(fileName, fileData, c)
+	newFile := RandStr(8) + ".jpg"
 
 	// Upload a file
 	var respHeader http.Header
@@ -926,7 +926,7 @@ func (s *OssDownloadSuite) TestdownloadFileChoiceOptions(c *C) {
 	os.Remove(newFile)
 	err = bucket.DeleteObject(objectName)
 	c.Assert(err, IsNil)
-	forceDeleteBucket(client, bucketName, c)
+	ForceDeleteBucket(client, bucketName, c)
 }
 
 func (s *OssDownloadSuite) TestdownloadFileWithCpChoiceOptions(c *C) {
@@ -934,18 +934,18 @@ func (s *OssDownloadSuite) TestdownloadFileWithCpChoiceOptions(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(6)
+	bucketName := bucketNamePrefix + RandLowStr(6)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 
 	bucket, err := client.Bucket(bucketName)
 
 	// begin test
-	objectName := objectNamePrefix + randStr(8)
-	fileName := "test-file-" + randStr(8)
-	fileData := randStr(500 * 1024)
-	createFile(fileName, fileData, c)
-	newFile := randStr(8) + ".jpg"
+	objectName := objectNamePrefix + RandStr(8)
+	fileName := "test-file-" + RandStr(8)
+	fileData := RandStr(500 * 1024)
+	CreateFile(fileName, fileData, c)
+	newFile := RandStr(8) + ".jpg"
 
 	// Upload a file
 	var respHeader http.Header
@@ -975,5 +975,5 @@ func (s *OssDownloadSuite) TestdownloadFileWithCpChoiceOptions(c *C) {
 	os.Remove(newFile)
 	err = bucket.DeleteObject(objectName)
 	c.Assert(err, IsNil)
-	forceDeleteBucket(client, bucketName, c)
+	ForceDeleteBucket(client, bucketName, c)
 }

+ 4 - 4
oss/error.go

@@ -54,9 +54,9 @@ func (e UnexpectedStatusCodeError) Got() int {
 	return e.got
 }
 
-// checkRespCode returns UnexpectedStatusError if the given response code is not
+// CheckRespCode returns UnexpectedStatusError if the given response code is not
 // one of the allowed status codes; otherwise nil.
-func checkRespCode(respCode int, allowed []int) error {
+func CheckRespCode(respCode int, allowed []int) error {
 	for _, v := range allowed {
 		if respCode == v {
 			return nil
@@ -79,14 +79,14 @@ func (e CRCCheckError) Error() string {
 		e.operation, e.clientCRC, e.serverCRC, e.requestID)
 }
 
-func checkDownloadCRC(clientCRC, serverCRC uint64) error {
+func CheckDownloadCRC(clientCRC, serverCRC uint64) error {
 	if clientCRC == serverCRC {
 		return nil
 	}
 	return CRCCheckError{clientCRC, serverCRC, "DownloadFile", ""}
 }
 
-func checkCRC(resp *Response, operation string) error {
+func CheckCRC(resp *Response, operation string) error {
 	if resp.Headers.Get(HTTPHeaderOssCRC64) == "" || resp.ClientCRC == resp.ServerCRC {
 		return nil
 	}

+ 6 - 6
oss/error_test.go

@@ -28,7 +28,7 @@ func (s *OssErrorSuite) TestCheckCRCHasCRCInResp(c *C) {
 		ServerCRC:  math.MaxUint64,
 	}
 
-	err := checkCRC(resp, "test")
+	err := CheckCRC(resp, "test")
 	c.Assert(err, IsNil)
 }
 
@@ -47,7 +47,7 @@ func (s *OssErrorSuite) TestCheckCRCNotHasCRCInResp(c *C) {
 		ServerCRC:  math.MaxUint32,
 	}
 
-	err := checkCRC(resp, "test")
+	err := CheckCRC(resp, "test")
 	c.Assert(err, IsNil)
 }
 
@@ -67,19 +67,19 @@ func (s *OssErrorSuite) TestCheckCRCCNegative(c *C) {
 		ServerCRC:  math.MaxUint64,
 	}
 
-	err := checkCRC(resp, "test")
+	err := CheckCRC(resp, "test")
 	c.Assert(err, NotNil)
 	testLogger.Println("error:", err)
 }
 
 func (s *OssErrorSuite) TestCheckDownloadCRC(c *C) {
-	err := checkDownloadCRC(0xFBF9D9603A6FA020, 0xFBF9D9603A6FA020)
+	err := CheckDownloadCRC(0xFBF9D9603A6FA020, 0xFBF9D9603A6FA020)
 	c.Assert(err, IsNil)
 
-	err = checkDownloadCRC(0, 0)
+	err = CheckDownloadCRC(0, 0)
 	c.Assert(err, IsNil)
 
-	err = checkDownloadCRC(0xDB6EFFF26AA94946, 0)
+	err = CheckDownloadCRC(0xDB6EFFF26AA94946, 0)
 	c.Assert(err, NotNil)
 	testLogger.Println("error:", err)
 }

+ 4 - 4
oss/livechannel.go

@@ -61,7 +61,7 @@ func (bucket Bucket) PutLiveChannelStatus(channelName, status string) error {
 	}
 	defer resp.Body.Close()
 
-	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 // PostVodPlaylist  create an playlist based on the specified playlist name, startTime and endTime
@@ -86,7 +86,7 @@ func (bucket Bucket) PostVodPlaylist(channelName, playlistName string, startTime
 	}
 	defer resp.Body.Close()
 
-	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 // GetVodPlaylist  get the playlist based on the specified channelName, startTime and endTime
@@ -196,7 +196,7 @@ func (bucket Bucket) GetLiveChannelHistory(channelName string) (LiveChannelHisto
 func (bucket Bucket) ListLiveChannel(options ...Option) (ListLiveChannelResult, error) {
 	var out ListLiveChannelResult
 
-	params, err := getRawParams(options)
+	params, err := GetRawParams(options)
 	if err != nil {
 		return out, err
 	}
@@ -234,7 +234,7 @@ func (bucket Bucket) DeleteLiveChannel(channelName string) error {
 	}
 	defer resp.Body.Close()
 
-	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
 }
 
 //

+ 4 - 4
oss/livechannel_test.go

@@ -91,7 +91,7 @@ func (s *OssBucketLiveChannelSuite) TestCreateLiveChannel(c *C) {
 	err = s.bucket.DeleteLiveChannel(channelName)
 	c.Assert(err, IsNil)
 
-	invalidType := randStr(4)
+	invalidType := RandStr(4)
 	invalidTarget := LiveChannelTarget{
 		PlaylistName: playlistName,
 		Type:         invalidType,
@@ -129,7 +129,7 @@ func (s *OssBucketLiveChannelSuite) TestDeleteLiveChannel(c *C) {
 	err = s.bucket.DeleteLiveChannel(emptyChannelName)
 	c.Assert(err, NotNil)
 
-	longChannelName := randStr(65)
+	longChannelName := RandStr(65)
 	err = s.bucket.DeleteLiveChannel(longChannelName)
 	c.Assert(err, NotNil)
 
@@ -197,7 +197,7 @@ func (s *OssBucketLiveChannelSuite) TestPutLiveChannelStatus(c *C) {
 	c.Assert(err, IsNil)
 	c.Assert("disabled", Equals, getCfg.Status)
 
-	invalidStatus := randLowStr(9)
+	invalidStatus := RandLowStr(9)
 	err = s.bucket.PutLiveChannelStatus(channelName, invalidStatus)
 	c.Assert(err, NotNil)
 
@@ -327,7 +327,7 @@ func (s *OssBucketLiveChannelSuite) TestListLiveChannel(c *C) {
 	ok = compareListResult(result, "", "", nextMarker, 100, true, 100)
 	c.Assert(ok, Equals, true)
 
-	randPrefix := randStr(5)
+	randPrefix := RandStr(5)
 	result, err = s.bucket.ListLiveChannel(Prefix(randPrefix))
 	c.Assert(err, IsNil)
 	ok = compareListResult(result, randPrefix, "", "", 100, false, 0)

+ 5 - 5
oss/multicopy.go

@@ -32,7 +32,7 @@ func (bucket Bucket) CopyFile(srcBucketName, srcObjectKey, destObjectKey string,
 	routines := getRoutines(options)
 
 	var strVersionId string
-	versionId, _ := findOption(options, "versionId", nil)
+	versionId, _ := FindOption(options, "versionId", nil)
 	if versionId != nil {
 		strVersionId = versionId.(string)
 	}
@@ -146,7 +146,7 @@ func (bucket Bucket) copyFile(srcBucketName, srcObjectKey, destBucketName, destO
 	partSize int64, options []Option, routines int) error {
 	descBucket, err := bucket.Client.Bucket(destBucketName)
 	srcBucket, err := bucket.Client.Bucket(srcBucketName)
-	listener := getProgressListener(options)
+	listener := GetProgressListener(options)
 
 	// choice valid options
 	headerOptions := ChoiceHeadObjectOption(options)
@@ -259,7 +259,7 @@ func (cp copyCheckpoint) isValid(meta http.Header) (bool, error) {
 		return false, nil
 	}
 
-	objectSize, err := strconv.ParseInt(meta.Get(HTTPHeaderContentLength), 10, 0)
+	objectSize, err := strconv.ParseInt(meta.Get(HTTPHeaderContentLength), 10, 64)
 	if err != nil {
 		return false, err
 	}
@@ -347,7 +347,7 @@ func (cp *copyCheckpoint) prepare(meta http.Header, srcBucket *Bucket, srcObject
 	cp.DestBucketName = destBucket.BucketName
 	cp.DestObjectKey = destObjectKey
 
-	objectSize, err := strconv.ParseInt(meta.Get(HTTPHeaderContentLength), 10, 0)
+	objectSize, err := strconv.ParseInt(meta.Get(HTTPHeaderContentLength), 10, 64)
 	if err != nil {
 		return err
 	}
@@ -390,7 +390,7 @@ func (bucket Bucket) copyFileWithCp(srcBucketName, srcObjectKey, destBucketName,
 	partSize int64, options []Option, cpFilePath string, routines int) error {
 	descBucket, err := bucket.Client.Bucket(destBucketName)
 	srcBucket, err := bucket.Client.Bucket(srcBucketName)
-	listener := getProgressListener(options)
+	listener := GetProgressListener(options)
 
 	// Load CP data
 	ccp := copyCheckpoint{}

+ 19 - 19
oss/multicopy_test.go

@@ -89,7 +89,7 @@ func (s *OssCopySuite) TearDownTest(c *C) {
 
 // TestCopyRoutineWithoutRecovery is multi-routine copy without resumable recovery
 func (s *OssCopySuite) TestCopyRoutineWithoutRecovery(c *C) {
-	srcObjectName := objectNamePrefix + randStr(8)
+	srcObjectName := objectNamePrefix + RandStr(8)
 	destObjectName := srcObjectName + "-dest"
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
 	newFile := "copy-new-file.jpg"
@@ -222,7 +222,7 @@ func CopyErrorHooker(part copyPart) error {
 
 // TestCopyRoutineWithoutRecoveryNegative is a multiple routines copy without checkpoint
 func (s *OssCopySuite) TestCopyRoutineWithoutRecoveryNegative(c *C) {
-	srcObjectName := objectNamePrefix + randStr(8)
+	srcObjectName := objectNamePrefix + RandStr(8)
 	destObjectName := srcObjectName + "-dest"
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
 
@@ -259,10 +259,10 @@ func (s *OssCopySuite) TestCopyRoutineWithoutRecoveryNegative(c *C) {
 
 // TestCopyRoutineWithRecovery is a multiple routines copy with resumable recovery
 func (s *OssCopySuite) TestCopyRoutineWithRecovery(c *C) {
-	srcObjectName := objectNamePrefix + randStr(8)
+	srcObjectName := objectNamePrefix + RandStr(8)
 	destObjectName := srcObjectName + "-dest"
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
-	newFile := randStr(8) + ".jpg"
+	newFile := RandStr(8) + ".jpg"
 
 	// Upload source file
 	err := s.bucket.UploadFile(srcObjectName, fileName, 100*1024, Routines(3))
@@ -425,7 +425,7 @@ func (s *OssCopySuite) TestCopyRoutineWithRecovery(c *C) {
 
 // TestCopyRoutineWithRecoveryNegative is a multiple routineed copy without checkpoint
 func (s *OssCopySuite) TestCopyRoutineWithRecoveryNegative(c *C) {
-	srcObjectName := objectNamePrefix + randStr(8)
+	srcObjectName := objectNamePrefix + RandStr(8)
 	destObjectName := srcObjectName + "-dest"
 
 	// Source bucket does not exist
@@ -448,10 +448,10 @@ func (s *OssCopySuite) TestCopyRoutineWithRecoveryNegative(c *C) {
 // TestCopyFileCrossBucket is a cross bucket's direct copy.
 func (s *OssCopySuite) TestCopyFileCrossBucket(c *C) {
 	destBucketName := bucketName + "-desc"
-	srcObjectName := objectNamePrefix + randStr(8)
+	srcObjectName := objectNamePrefix + RandStr(8)
 	destObjectName := srcObjectName + "-dest"
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
-	newFile := randStr(8) + ".jpg"
+	newFile := RandStr(8) + ".jpg"
 
 	destBucket, err := s.client.Bucket(destBucketName)
 	c.Assert(err, IsNil)
@@ -495,7 +495,7 @@ func (s *OssCopySuite) TestCopyFileCrossBucket(c *C) {
 	os.Remove(newFile)
 
 	// Delete target bucket
-	forceDeleteBucket(s.client, destBucketName, c)
+	ForceDeleteBucket(s.client, destBucketName, c)
 }
 
 func (s *OssCopySuite) TestVersioningCopyFileCrossBucket(c *C) {
@@ -503,7 +503,7 @@ func (s *OssCopySuite) TestVersioningCopyFileCrossBucket(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(6)
+	bucketName := bucketNamePrefix + RandLowStr(6)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 
@@ -516,13 +516,13 @@ func (s *OssCopySuite) TestVersioningCopyFileCrossBucket(c *C) {
 	c.Assert(err, IsNil)
 
 	// begin test
-	objectName := objectNamePrefix + randStr(8)
-	fileName := "test-file-" + randStr(8)
-	fileData := randStr(500 * 1024)
-	createFile(fileName, fileData, c)
-	newFile := "test-file-" + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
+	fileName := "test-file-" + RandStr(8)
+	fileData := RandStr(500 * 1024)
+	CreateFile(fileName, fileData, c)
+	newFile := "test-file-" + RandStr(8)
 	destBucketName := bucketName + "-desc"
-	srcObjectName := objectNamePrefix + randStr(8)
+	srcObjectName := objectNamePrefix + RandStr(8)
 	destObjectName := srcObjectName + "-dest"
 
 	// Create dest bucket
@@ -584,17 +584,17 @@ func (s *OssCopySuite) TestVersioningCopyFileCrossBucket(c *C) {
 	os.Remove(newFile)
 	destBucket.DeleteObject(destObjectName)
 	bucket.DeleteObject(objectName)
-	forceDeleteBucket(client, bucketName, c)
-	forceDeleteBucket(client, destBucketName, c)
+	ForceDeleteBucket(client, bucketName, c)
+	ForceDeleteBucket(client, destBucketName, c)
 }
 
 // TestCopyFileChoiceOptions
 func (s *OssCopySuite) TestCopyFileChoiceOptions(c *C) {
 	destBucketName := bucketName + "-desc"
-	srcObjectName := objectNamePrefix + randStr(8)
+	srcObjectName := objectNamePrefix + RandStr(8)
 	destObjectName := srcObjectName + "-dest"
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
-	newFile := randStr(8) + ".jpg"
+	newFile := RandStr(8) + ".jpg"
 
 	destBucket, err := s.client.Bucket(destBucketName)
 	c.Assert(err, IsNil)

+ 11 - 11
oss/multipart.go

@@ -23,8 +23,8 @@ import (
 //
 func (bucket Bucket) InitiateMultipartUpload(objectKey string, options ...Option) (InitiateMultipartUploadResult, error) {
 	var imur InitiateMultipartUploadResult
-	opts := addContentType(options, objectKey)
-	params, _ := getRawParams(options)
+	opts := AddContentType(options, objectKey)
+	params, _ := GetRawParams(options)
 	_, ok := params["sequential"]
 	if ok {
 		// convert "" to nil
@@ -112,7 +112,7 @@ func (bucket Bucket) UploadPartFromFile(imur InitiateMultipartUploadResult, file
 // error    it's nil if the operation succeeds, otherwise it's an error object.
 //
 func (bucket Bucket) DoUploadPart(request *UploadPartRequest, options []Option) (*UploadPartResult, error) {
-	listener := getProgressListener(options)
+	listener := GetProgressListener(options)
 	options = append(options, ContentLength(request.PartSize))
 	params := map[string]interface{}{}
 	params["partNumber"] = strconv.Itoa(request.PartNumber)
@@ -129,8 +129,8 @@ func (bucket Bucket) DoUploadPart(request *UploadPartRequest, options []Option)
 		PartNumber: request.PartNumber,
 	}
 
-	if bucket.getConfig().IsEnableCRC {
-		err = checkCRC(resp, "DoUploadPart")
+	if bucket.GetConfig().IsEnableCRC {
+		err = CheckCRC(resp, "DoUploadPart")
 		if err != nil {
 			return &UploadPartResult{part}, err
 		}
@@ -161,14 +161,14 @@ func (bucket Bucket) UploadPartCopy(imur InitiateMultipartUploadResult, srcBucke
 
 	//first find version id
 	versionIdKey := "versionId"
-	versionId, _ := findOption(options, versionIdKey, nil)
+	versionId, _ := FindOption(options, versionIdKey, nil)
 	if versionId == nil {
 		opts = []Option{CopySource(srcBucketName, url.QueryEscape(srcObjectKey)),
 			CopySourceRange(startPosition, partSize)}
 	} else {
 		opts = []Option{CopySourceVersion(srcBucketName, url.QueryEscape(srcObjectKey), versionId.(string)),
 			CopySourceRange(startPosition, partSize)}
-		options = deleteOption(options, versionIdKey)
+		options = DeleteOption(options, versionIdKey)
 	}
 
 	opts = append(opts, options...)
@@ -204,7 +204,7 @@ func (bucket Bucket) CompleteMultipartUpload(imur InitiateMultipartUploadResult,
 	parts []UploadPart, options ...Option) (CompleteMultipartUploadResult, error) {
 	var out CompleteMultipartUploadResult
 
-	sort.Sort(uploadParts(parts))
+	sort.Sort(UploadParts(parts))
 	cxml := completeMultipartUploadXML{}
 	cxml.Part = parts
 	bs, err := xml.Marshal(cxml)
@@ -240,7 +240,7 @@ func (bucket Bucket) AbortMultipartUpload(imur InitiateMultipartUploadResult, op
 		return err
 	}
 	defer resp.Body.Close()
-	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+	return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
 }
 
 // ListUploadedParts lists the uploaded parts.
@@ -255,7 +255,7 @@ func (bucket Bucket) ListUploadedParts(imur InitiateMultipartUploadResult, optio
 	options = append(options, EncodingType("url"))
 
 	params := map[string]interface{}{}
-	params, err := getRawParams(options)
+	params, err := GetRawParams(options)
 	if err != nil {
 		return out, err
 	}
@@ -287,7 +287,7 @@ func (bucket Bucket) ListMultipartUploads(options ...Option) (ListMultipartUploa
 	var out ListMultipartUploadResult
 
 	options = append(options, EncodingType("url"))
-	params, err := getRawParams(options)
+	params, err := GetRawParams(options)
 	if err != nil {
 		return out, err
 	}

+ 24 - 24
oss/multipart_test.go

@@ -133,7 +133,7 @@ func (s *OssBucketMultipartSuite) TearDownTest(c *C) {
 
 // TestMultipartUpload
 func (s *OssBucketMultipartSuite) TestMultipartUpload(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	var fileName = "../sample/BingWallpaper-2015-11-07.jpg"
 
 	chunks, err := SplitFileByPartNum(fileName, 3)
@@ -178,7 +178,7 @@ func (s *OssBucketMultipartSuite) TestMultipartUpload(c *C) {
 
 // TestMultipartUploadFromFile
 func (s *OssBucketMultipartSuite) TestMultipartUploadFromFile(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	var fileName = "../sample/BingWallpaper-2015-11-07.jpg"
 
 	chunks, err := SplitFileByPartNum(fileName, 3)
@@ -217,8 +217,8 @@ func (s *OssBucketMultipartSuite) TestMultipartUploadFromFile(c *C) {
 
 // TestUploadPartCopy
 func (s *OssBucketMultipartSuite) TestUploadPartCopy(c *C) {
-	objectSrc := objectNamePrefix + randStr(8) + "-src"
-	objectDest := objectNamePrefix + randStr(8) + "-dest"
+	objectSrc := objectNamePrefix + RandStr(8) + "-src"
+	objectDest := objectNamePrefix + RandStr(8) + "-dest"
 	var fileName = "../sample/BingWallpaper-2015-11-07.jpg"
 
 	chunks, err := SplitFileByPartNum(fileName, 3)
@@ -261,7 +261,7 @@ func (s *OssBucketMultipartSuite) TestUploadPartCopy(c *C) {
 }
 
 func (s *OssBucketMultipartSuite) TestListUploadedParts(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	objectSrc := objectName + "-src"
 	objectDest := objectName + "-dest"
 	var fileName = "../sample/BingWallpaper-2015-11-07.jpg"
@@ -327,7 +327,7 @@ func (s *OssBucketMultipartSuite) TestListUploadedParts(c *C) {
 }
 
 func (s *OssBucketMultipartSuite) TestAbortMultipartUpload(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	objectSrc := objectName + "-src"
 	objectDest := objectName + "-dest"
 	var fileName = "../sample/BingWallpaper-2015-11-07.jpg"
@@ -393,8 +393,8 @@ func (s *OssBucketMultipartSuite) TestAbortMultipartUpload(c *C) {
 
 // TestUploadPartCopyWithConstraints
 func (s *OssBucketMultipartSuite) TestUploadPartCopyWithConstraints(c *C) {
-	objectSrc := objectNamePrefix + randStr(8) + "-src"
-	objectDest := objectNamePrefix + randStr(8) + "-dest"
+	objectSrc := objectNamePrefix + RandStr(8) + "-src"
+	objectDest := objectNamePrefix + RandStr(8) + "-dest"
 	var fileName = "../sample/BingWallpaper-2015-11-07.jpg"
 
 	chunks, err := SplitFileByPartNum(fileName, 3)
@@ -450,7 +450,7 @@ func (s *OssBucketMultipartSuite) TestUploadPartCopyWithConstraints(c *C) {
 
 // TestMultipartUploadFromFileOutofOrder
 func (s *OssBucketMultipartSuite) TestMultipartUploadFromFileOutofOrder(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	var fileName = "../sample/BingWallpaper-2015-11-07.jpg"
 
 	chunks, err := SplitFileByPartSize(fileName, 1024*100)
@@ -484,8 +484,8 @@ func (s *OssBucketMultipartSuite) TestMultipartUploadFromFileOutofOrder(c *C) {
 
 // TestUploadPartCopyOutofOrder
 func (s *OssBucketMultipartSuite) TestUploadPartCopyOutofOrder(c *C) {
-	objectSrc := objectNamePrefix + randStr(8) + "-src"
-	objectDest := objectNamePrefix + randStr(8) + "-dest"
+	objectSrc := objectNamePrefix + RandStr(8) + "-src"
+	objectDest := objectNamePrefix + RandStr(8) + "-dest"
 	var fileName = "../sample/BingWallpaper-2015-11-07.jpg"
 
 	chunks, err := SplitFileByPartSize(fileName, 1024*100)
@@ -524,7 +524,7 @@ func (s *OssBucketMultipartSuite) TestUploadPartCopyOutofOrder(c *C) {
 
 // TestMultipartUploadFromFileType
 func (s *OssBucketMultipartSuite) TestMultipartUploadFromFileType(c *C) {
-	objectName := objectNamePrefix + randStr(8) + ".jpg"
+	objectName := objectNamePrefix + RandStr(8) + ".jpg"
 	var fileName = "../sample/BingWallpaper-2015-11-07.jpg"
 
 	chunks, err := SplitFileByPartNum(fileName, 4)
@@ -556,7 +556,7 @@ func (s *OssBucketMultipartSuite) TestMultipartUploadFromFileType(c *C) {
 }
 
 func (s *OssBucketMultipartSuite) TestListMultipartUploads(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 
 	imurs := []InitiateMultipartUploadResult{}
 	for i := 0; i < 20; i++ {
@@ -598,7 +598,7 @@ func (s *OssBucketMultipartSuite) TestListMultipartUploads(c *C) {
 	c.Assert(len(lmpu.Uploads), Equals, 18)
 	c.Assert(len(lmpu.CommonPrefixes), Equals, 2)
 
-	upLoadIDStr := randStr(3)
+	upLoadIDStr := RandStr(3)
 	lmpu, err = s.bucket.ListMultipartUploads(KeyMarker(objectName+"12"), UploadIDMarker(upLoadIDStr))
 	c.Assert(err, IsNil)
 	checkNum := 15
@@ -618,7 +618,7 @@ func (s *OssBucketMultipartSuite) TestListMultipartUploads(c *C) {
 }
 
 func (s *OssBucketMultipartSuite) TestListMultipartUploadsEncodingKey(c *C) {
-	prefix := objectNamePrefix + "让你任性让你狂" + randStr(8)
+	prefix := objectNamePrefix + "让你任性让你狂" + RandStr(8)
 
 	imurs := []InitiateMultipartUploadResult{}
 	for i := 0; i < 3; i++ {
@@ -652,7 +652,7 @@ func (s *OssBucketMultipartSuite) TestListMultipartUploadsEncodingKey(c *C) {
 }
 
 func (s *OssBucketMultipartSuite) TestMultipartNegative(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 
 	// Key tool long
 	data := make([]byte, 100*1024)
@@ -711,7 +711,7 @@ func (s *OssBucketMultipartSuite) TestMultipartNegative(c *C) {
 }
 
 func (s *OssBucketMultipartSuite) TestMultipartUploadFromFileBigFile(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	bigFile := "D:\\tmp\\bigfile.zip"
 	newFile := "D:\\tmp\\newbigfile.zip"
 
@@ -759,9 +759,9 @@ func (s *OssBucketMultipartSuite) TestMultipartUploadFromFileBigFile(c *C) {
 
 // TestUploadFile
 func (s *OssBucketMultipartSuite) TestUploadFile(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	var fileName = "../sample/BingWallpaper-2015-11-07.jpg"
-	newFile := randStr(8) + ".jpg"
+	newFile := RandStr(8) + ".jpg"
 
 	// Upload with 100K part size
 	err := s.bucket.UploadFile(objectName, fileName, 100*1024)
@@ -852,7 +852,7 @@ func (s *OssBucketMultipartSuite) TestUploadFile(c *C) {
 }
 
 func (s *OssBucketMultipartSuite) TestUploadFileNegative(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	var fileName = "../sample/BingWallpaper-2015-11-07.jpg"
 
 	// Smaller than the required minimal part size (100KB)
@@ -874,9 +874,9 @@ func (s *OssBucketMultipartSuite) TestUploadFileNegative(c *C) {
 
 // TestDownloadFile
 func (s *OssBucketMultipartSuite) TestDownloadFile(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	var fileName = "../sample/BingWallpaper-2015-11-07.jpg"
-	newFile := randStr(8) + ".jpg"
+	newFile := RandStr(8) + ".jpg"
 
 	err := s.bucket.UploadFile(objectName, fileName, 100*1024)
 	c.Assert(err, IsNil)
@@ -956,8 +956,8 @@ func (s *OssBucketMultipartSuite) TestDownloadFile(c *C) {
 }
 
 func (s *OssBucketMultipartSuite) TestDownloadFileNegative(c *C) {
-	objectName := objectNamePrefix + randStr(8)
-	newFile := randStr(8) + ".jpg"
+	objectName := objectNamePrefix + RandStr(8)
+	newFile := RandStr(8) + ".jpg"
 
 	// Smaller than the required minimal part size (100KB)
 	err := s.bucket.DownloadFile(objectName, newFile, 100*1024-1)

+ 9 - 4
oss/option.go

@@ -251,6 +251,11 @@ func TrafficLimitHeader(value int64) Option {
 	return setHeader(HTTPHeaderOssTrafficLimit, strconv.FormatInt(value, 10))
 }
 
+// UserAgentHeader is an option to set HTTPHeaderUserAgent
+func UserAgentHeader(ua string) Option {
+	return setHeader(HTTPHeaderUserAgent, ua)
+}
+
 // ForbidOverWrite  is an option to set X-Oss-Forbid-Overwrite
 func ForbidOverWrite(forbidWrite bool) Option {
 	if forbidWrite {
@@ -490,7 +495,7 @@ func handleOptions(headers map[string]string, options []Option) error {
 	return nil
 }
 
-func getRawParams(options []Option) (map[string]interface{}, error) {
+func GetRawParams(options []Option) (map[string]interface{}, error) {
 	// Option
 	params := map[string]optionValue{}
 	for _, option := range options {
@@ -513,7 +518,7 @@ func getRawParams(options []Option) (map[string]interface{}, error) {
 	return paramsm, nil
 }
 
-func findOption(options []Option, param string, defaultVal interface{}) (interface{}, error) {
+func FindOption(options []Option, param string, defaultVal interface{}) (interface{}, error) {
 	params := map[string]optionValue{}
 	for _, option := range options {
 		if option != nil {
@@ -529,7 +534,7 @@ func findOption(options []Option, param string, defaultVal interface{}) (interfa
 	return defaultVal, nil
 }
 
-func isOptionSet(options []Option, option string) (bool, interface{}, error) {
+func IsOptionSet(options []Option, option string) (bool, interface{}, error) {
 	params := map[string]optionValue{}
 	for _, option := range options {
 		if option != nil {
@@ -545,7 +550,7 @@ func isOptionSet(options []Option, option string) (bool, interface{}, error) {
 	return false, nil, nil
 }
 
-func deleteOption(options []Option, strKey string) []Option {
+func DeleteOption(options []Option, strKey string) []Option {
 	var outOption []Option
 	params := map[string]optionValue{}
 	for _, option := range options {

+ 9 - 9
oss/option_test.go

@@ -267,7 +267,7 @@ func (s *OssOptionSuite) TestHandleParams(c *C) {
 		options = append(options, testcase.option)
 	}
 
-	params, err := getRawParams(options)
+	params, err := GetRawParams(options)
 	c.Assert(err, IsNil)
 
 	out := client.Conn.getURLParams(params)
@@ -275,7 +275,7 @@ func (s *OssOptionSuite) TestHandleParams(c *C) {
 
 	options = []Option{KeyMarker(""), nil}
 
-	params, err = getRawParams(options)
+	params, err = GetRawParams(options)
 	c.Assert(err, IsNil)
 
 	out = client.Conn.getURLParams(params)
@@ -289,29 +289,29 @@ func (s *OssOptionSuite) TestFindOption(c *C) {
 		options = append(options, testcase.option)
 	}
 
-	str, err := findOption(options, "X-Oss-Acl", "")
+	str, err := FindOption(options, "X-Oss-Acl", "")
 	c.Assert(err, IsNil)
 	c.Assert(str, Equals, "private")
 
-	str, err = findOption(options, "MyProp", "")
+	str, err = FindOption(options, "MyProp", "")
 	c.Assert(err, IsNil)
 	c.Assert(str, Equals, "")
 }
 
 func (s *OssOptionSuite) TestDeleteOption(c *C) {
 	options := []Option{VersionId("123"), VersionIdMarker("456"), KeyMarker("789")}
-	str, err := findOption(options, "versionId", "")
+	str, err := FindOption(options, "versionId", "")
 	c.Assert(str, Equals, "123")
 	c.Assert(err, IsNil)
 
-	skipOption := deleteOption(options, "versionId")
-	str, err = findOption(skipOption, "versionId", "")
+	skipOption := DeleteOption(options, "versionId")
+	str, err = FindOption(skipOption, "versionId", "")
 	c.Assert(str, Equals, "")
 
-	str, err = findOption(skipOption, "version-id-marker", "")
+	str, err = FindOption(skipOption, "version-id-marker", "")
 	c.Assert(str, Equals, "456")
 
-	str, err = findOption(skipOption, "key-marker", "")
+	str, err = FindOption(skipOption, "key-marker", "")
 	c.Assert(str, Equals, "789")
 
 }

+ 16 - 16
oss/progress_test.go

@@ -128,7 +128,7 @@ func (listener *OssProgressListener) ProgressChanged(event *ProgressEvent) {
 
 // TestPutObject
 func (s *OssProgressSuite) TestPutObject(c *C) {
-	objectName := randStr(8) + ".jpg"
+	objectName := RandStr(8) + ".jpg"
 	localFile := "../sample/The Go Programming Language.html"
 
 	fileInfo, err := os.Stat(localFile)
@@ -177,10 +177,10 @@ func (s *OssProgressSuite) TestPutObject(c *C) {
 
 // TestSignURL
 func (s *OssProgressSuite) SignURLTestFunc(c *C, authVersion AuthVersionType, extraHeaders []string) {
-	objectName := objectNamePrefix + randStr(8)
-	filePath := randLowStr(10)
-	content := randStr(20)
-	createFile(filePath, content, c)
+	objectName := objectNamePrefix + RandStr(8)
+	filePath := RandLowStr(10)
+	content := RandStr(20)
+	CreateFile(filePath, content, c)
 
 	oldType := s.bucket.Client.Config.AuthVersion
 	oldHeaders := s.bucket.Client.Config.AdditionalHeaders
@@ -256,7 +256,7 @@ func (s *OssProgressSuite) SignURLTestFunc(c *C, authVersion AuthVersionType, ex
 	str, err = s.bucket.SignURL(objectName, HTTPGet, 10, Progress(&progressListener))
 	c.Assert(err, IsNil)
 
-	newFile := randStr(10)
+	newFile := RandStr(10)
 	progressListener.TotalRwBytes = 0
 	err = s.bucket.GetObjectToFileWithURL(str, newFile, Progress(&progressListener))
 	c.Assert(progressListener.TotalRwBytes, Equals, int64(len(content)))
@@ -284,7 +284,7 @@ func (s *OssProgressSuite) TestSignURL(c *C) {
 }
 
 func (s *OssProgressSuite) TestPutObjectNegative(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	localFile := "../sample/The Go Programming Language.html"
 
 	// Invalid endpoint
@@ -303,8 +303,8 @@ func (s *OssProgressSuite) TestPutObjectNegative(c *C) {
 
 // TestAppendObject
 func (s *OssProgressSuite) TestAppendObject(c *C) {
-	objectName := objectNamePrefix + randStr(8)
-	objectValue := randStr(100)
+	objectName := objectNamePrefix + RandStr(8)
+	objectValue := RandStr(100)
 	var val = []byte(objectValue)
 	var nextPos int64
 	var midPos = 1 + rand.Intn(len(val)-1)
@@ -330,7 +330,7 @@ func (s *OssProgressSuite) TestAppendObject(c *C) {
 
 // TestMultipartUpload
 func (s *OssProgressSuite) TestMultipartUpload(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	var fileName = "../sample/BingWallpaper-2015-11-07.jpg"
 
 	fileInfo, err := os.Stat(fileName)
@@ -371,7 +371,7 @@ func (s *OssProgressSuite) TestMultipartUpload(c *C) {
 
 // TestMultipartUploadFromFile
 func (s *OssProgressSuite) TestMultipartUploadFromFile(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	var fileName = "../sample/BingWallpaper-2015-11-07.jpg"
 	fileInfo, err := os.Stat(fileName)
 	c.Assert(err, IsNil)
@@ -405,7 +405,7 @@ func (s *OssProgressSuite) TestMultipartUploadFromFile(c *C) {
 
 // TestGetObject
 func (s *OssProgressSuite) TestGetObject(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	localFile := "../sample/BingWallpaper-2015-11-07.jpg"
 	newFile := "newpic-progress-1.jpg"
 
@@ -473,7 +473,7 @@ func (s *OssProgressSuite) TestGetObject(c *C) {
 
 // TestGetObjectNegative
 func (s *OssProgressSuite) TestGetObjectNegative(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	localFile := "../sample/BingWallpaper-2015-11-07.jpg"
 
 	// PutObject
@@ -503,7 +503,7 @@ func (s *OssProgressSuite) TestGetObjectNegative(c *C) {
 
 // TestUploadFile
 func (s *OssProgressSuite) TestUploadFile(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
 
 	fileInfo, err := os.Stat(fileName)
@@ -524,7 +524,7 @@ func (s *OssProgressSuite) TestUploadFile(c *C) {
 
 // TestDownloadFile
 func (s *OssProgressSuite) TestDownloadFile(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
 	newFile := "down-new-file-progress-2.jpg"
 
@@ -555,7 +555,7 @@ func (s *OssProgressSuite) TestDownloadFile(c *C) {
 
 // TestCopyFile
 func (s *OssProgressSuite) TestCopyFile(c *C) {
-	srcObjectName := objectNamePrefix + randStr(8)
+	srcObjectName := objectNamePrefix + RandStr(8)
 	destObjectName := srcObjectName + "-copy"
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
 

+ 3 - 3
oss/select_object.go

@@ -134,17 +134,17 @@ func (bucket Bucket) DoPostSelectObject(key string, params map[string]interface{
 	}
 	result.Headers = resp.Headers
 	// result.Frame = SelectObjectResult{}
-	result.ReadTimeOut = bucket.getConfig().Timeout
+	result.ReadTimeOut = bucket.GetConfig().Timeout
 
 	// Progress
-	listener := getProgressListener(options)
+	listener := GetProgressListener(options)
 
 	// CRC32
 	crcCalc := crc32.NewIEEE()
 	result.WriterForCheckCrc32 = crcCalc
 	result.Body = TeeReader(resp.Body, nil, 0, listener, nil)
 
-	err = checkRespCode(resp.StatusCode, []int{http.StatusPartialContent, http.StatusOK})
+	err = CheckRespCode(resp.StatusCode, []int{http.StatusPartialContent, http.StatusOK})
 
 	return result, err
 }

+ 4 - 4
oss/type.go

@@ -446,17 +446,17 @@ type UploadPart struct {
 	ETag       string   `xml:"ETag"`       // ETag value of the part's data
 }
 
-type uploadParts []UploadPart
+type UploadParts []UploadPart
 
-func (slice uploadParts) Len() int {
+func (slice UploadParts) Len() int {
 	return len(slice)
 }
 
-func (slice uploadParts) Less(i, j int) bool {
+func (slice UploadParts) Less(i, j int) bool {
 	return slice[i].PartNumber < slice[j].PartNumber
 }
 
-func (slice uploadParts) Swap(i, j int) {
+func (slice UploadParts) Swap(i, j int) {
 	slice[i], slice[j] = slice[j], slice[i]
 }
 

+ 2 - 2
oss/type_test.go

@@ -82,7 +82,7 @@ func (s *OssTypeSuite) TestDecodeListMultipartUploadResult(c *C) {
 func (s *OssTypeSuite) TestSortUploadPart(c *C) {
 	parts := []UploadPart{}
 
-	sort.Sort(uploadParts(parts))
+	sort.Sort(UploadParts(parts))
 	c.Assert(len(parts), Equals, 0)
 
 	parts = []UploadPart{
@@ -93,7 +93,7 @@ func (s *OssTypeSuite) TestSortUploadPart(c *C) {
 		{PartNumber: 3, ETag: "E3"},
 	}
 
-	sort.Sort(uploadParts(parts))
+	sort.Sort(UploadParts(parts))
 
 	c.Assert(parts[0].PartNumber, Equals, 1)
 	c.Assert(parts[0].ETag, Equals, "E1")

+ 8 - 9
oss/upload.go

@@ -54,7 +54,7 @@ func getUploadCpFilePath(cpConf *cpConfig, srcFile, destBucket, destObject strin
 
 // getCpConfig gets checkpoint configuration
 func getCpConfig(options []Option) *cpConfig {
-	cpcOpt, err := findOption(options, checkpointConfig, nil)
+	cpcOpt, err := FindOption(options, checkpointConfig, nil)
 	if err != nil || cpcOpt == nil {
 		return nil
 	}
@@ -84,7 +84,7 @@ func getCpFileName(src, dest, versionId string) string {
 
 // getRoutines gets the routine count. by default it's 1.
 func getRoutines(options []Option) int {
-	rtnOpt, err := findOption(options, routineNum, nil)
+	rtnOpt, err := FindOption(options, routineNum, nil)
 	if err != nil || rtnOpt == nil {
 		return 1
 	}
@@ -101,17 +101,16 @@ func getRoutines(options []Option) int {
 
 // getPayer return the payer of the request
 func getPayer(options []Option) string {
-	payerOpt, err := findOption(options, HTTPHeaderOssRequester, nil)
+	payerOpt, err := FindOption(options, HTTPHeaderOssRequester, nil)
 	if err != nil || payerOpt == nil {
 		return ""
 	}
-
 	return payerOpt.(string)
 }
 
-// getProgressListener gets the progress callback
-func getProgressListener(options []Option) ProgressListener {
-	isSet, listener, _ := isOptionSet(options, progressListener)
+// GetProgressListener gets the progress callback
+func GetProgressListener(options []Option) ProgressListener {
+	isSet, listener, _ := IsOptionSet(options, progressListener)
 	if !isSet {
 		return nil
 	}
@@ -188,7 +187,7 @@ func getTotalBytes(chunks []FileChunk) int64 {
 
 // uploadFile is a concurrent upload, without checkpoint
 func (bucket Bucket) uploadFile(objectKey, filePath string, partSize int64, options []Option, routines int) error {
-	listener := getProgressListener(options)
+	listener := GetProgressListener(options)
 
 	chunks, err := SplitFileByPartSize(filePath, partSize)
 	if err != nil {
@@ -467,7 +466,7 @@ func complete(cp *uploadCheckpoint, bucket *Bucket, parts []UploadPart, cpFilePa
 
 // uploadFileWithCp handles concurrent upload with checkpoint
 func (bucket Bucket) uploadFileWithCp(objectKey, filePath string, partSize int64, options []Option, cpFilePath string, routines int) error {
-	listener := getProgressListener(options)
+	listener := GetProgressListener(options)
 
 	partOptions := ChoiceTransferPartOption(options)
 	completeOptions := ChoiceCompletePartOption(options)

+ 30 - 30
oss/upload_test.go

@@ -89,9 +89,9 @@ func (s *OssUploadSuite) TearDownTest(c *C) {
 
 // TestUploadRoutineWithoutRecovery tests multiroutineed upload without checkpoint
 func (s *OssUploadSuite) TestUploadRoutineWithoutRecovery(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
-	newFile := randStr(8) + ".jpg"
+	newFile := RandStr(8) + ".jpg"
 
 	// Routines is not specified, by default single routine
 	err := s.bucket.UploadFile(objectName, fileName, 100*1024)
@@ -224,7 +224,7 @@ func ErrorHooker(id int, chunk FileChunk) error {
 
 // TestUploadRoutineWithoutRecoveryNegative is multiroutineed upload without checkpoint
 func (s *OssUploadSuite) TestUploadRoutineWithoutRecoveryNegative(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
 
 	uploadPartHooker = ErrorHooker
@@ -248,7 +248,7 @@ func (s *OssUploadSuite) TestUploadRoutineWithoutRecoveryNegative(c *C) {
 
 // TestUploadRoutineWithRecovery is multi-routine upload with resumable recovery
 func (s *OssUploadSuite) TestUploadRoutineWithRecovery(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
 	newFile := "upload-new-file-2.jpg"
 
@@ -397,7 +397,7 @@ func (s *OssUploadSuite) TestUploadRoutineWithRecovery(c *C) {
 
 // TestUploadRoutineWithRecoveryNegative is multiroutineed upload without checkpoint
 func (s *OssUploadSuite) TestUploadRoutineWithRecoveryNegative(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
 
 	// The local file does not exist
@@ -423,10 +423,10 @@ func (s *OssUploadSuite) TestUploadRoutineWithRecoveryNegative(c *C) {
 
 // TestUploadLocalFileChange tests the file is updated while being uploaded
 func (s *OssUploadSuite) TestUploadLocalFileChange(c *C) {
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
-	localFile := randStr(8) + ".jpg"
-	newFile := randStr(8) + ".jpg"
+	localFile := RandStr(8) + ".jpg"
+	newFile := RandStr(8) + ".jpg"
 
 	os.Remove(localFile)
 	err := copyFile(fileName, localFile)
@@ -465,11 +465,11 @@ func (s *OssUploadSuite) TestUploadPartArchiveObject(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(6)
+	bucketName := bucketNamePrefix + RandLowStr(6)
 	err = client.CreateBucket(bucketName, StorageClass(StorageArchive))
 	c.Assert(err, IsNil)
 	bucket, err := client.Bucket(bucketName)
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
 	fileInfo, err := os.Stat(fileName)
@@ -482,7 +482,7 @@ func (s *OssUploadSuite) TestUploadPartArchiveObject(c *C) {
 	// Updating the file,archive object,checkpoint
 	err = bucket.UploadFile(objectName, fileName, fileInfo.Size()/2, ObjectStorageClass(StorageArchive), Checkpoint(true, fileName+".cp"))
 	c.Assert(err, IsNil)
-	forceDeleteBucket(client, bucketName, c)
+	ForceDeleteBucket(client, bucketName, c)
 }
 
 func copyFile(src, dst string) error {
@@ -507,7 +507,7 @@ func (s *OssUploadSuite) TestVersioningUploadRoutineWithRecovery(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(6)
+	bucketName := bucketNamePrefix + RandLowStr(6)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 
@@ -520,11 +520,11 @@ func (s *OssUploadSuite) TestVersioningUploadRoutineWithRecovery(c *C) {
 	c.Assert(err, IsNil)
 
 	// begin test
-	objectName := objectNamePrefix + randStr(8)
-	fileName := "test-file-" + randStr(8)
-	fileData := randStr(500 * 1024)
-	createFile(fileName, fileData, c)
-	newFile := "test-file-" + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
+	fileName := "test-file-" + RandStr(8)
+	fileData := RandStr(500 * 1024)
+	CreateFile(fileName, fileData, c)
+	newFile := "test-file-" + RandStr(8)
 
 	// Use default routines and default CP file path (fileName+.cp)Header
 	// First upload for 4 parts
@@ -559,7 +559,7 @@ func (s *OssUploadSuite) TestVersioningUploadRoutineWithRecovery(c *C) {
 	os.Remove(fileName)
 	os.Remove(newFile)
 	bucket.DeleteObject(objectName)
-	forceDeleteBucket(client, bucketName, c)
+	ForceDeleteBucket(client, bucketName, c)
 }
 
 // TestUploadFileChoiceOptions
@@ -568,7 +568,7 @@ func (s *OssUploadSuite) TestUploadFileChoiceOptions(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(6)
+	bucketName := bucketNamePrefix + RandLowStr(6)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 	bucket, err := client.Bucket(bucketName)
@@ -577,7 +577,7 @@ func (s *OssUploadSuite) TestUploadFileChoiceOptions(c *C) {
 	fileInfo, err := os.Stat(fileName)
 	c.Assert(err, IsNil)
 
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 
 	// UploadFile with properties
 	options := []Option{
@@ -600,7 +600,7 @@ func (s *OssUploadSuite) TestUploadFileChoiceOptions(c *C) {
 	aclResult, err := bucket.GetObjectACL(objectName)
 	c.Assert(aclResult.ACL, Equals, "public-read")
 	c.Assert(err, IsNil)
-	forceDeleteBucket(client, bucketName, c)
+	ForceDeleteBucket(client, bucketName, c)
 }
 
 // TestUploadFileWithCpChoiceOptions
@@ -609,7 +609,7 @@ func (s *OssUploadSuite) TestUploadFileWithCpChoiceOptions(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(6)
+	bucketName := bucketNamePrefix + RandLowStr(6)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 	bucket, err := client.Bucket(bucketName)
@@ -618,7 +618,7 @@ func (s *OssUploadSuite) TestUploadFileWithCpChoiceOptions(c *C) {
 	fileInfo, err := os.Stat(fileName)
 	c.Assert(err, IsNil)
 
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 
 	// UploadFile with properties
 	options := []Option{
@@ -645,7 +645,7 @@ func (s *OssUploadSuite) TestUploadFileWithCpChoiceOptions(c *C) {
 	c.Assert(aclResult.ACL, Equals, "public-read")
 	c.Assert(err, IsNil)
 
-	forceDeleteBucket(client, bucketName, c)
+	ForceDeleteBucket(client, bucketName, c)
 }
 
 // TestUploadFileWithForbidOverWrite
@@ -654,7 +654,7 @@ func (s *OssUploadSuite) TestUploadFileWithForbidOverWrite(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(6)
+	bucketName := bucketNamePrefix + RandLowStr(6)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 	bucket, err := client.Bucket(bucketName)
@@ -663,7 +663,7 @@ func (s *OssUploadSuite) TestUploadFileWithForbidOverWrite(c *C) {
 	fileInfo, err := os.Stat(fileName)
 	c.Assert(err, IsNil)
 
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 
 	// UploadFile with properties
 	options := []Option{
@@ -698,7 +698,7 @@ func (s *OssUploadSuite) TestUploadFileWithForbidOverWrite(c *C) {
 	err = bucket.UploadFile(objectName, fileName, fileInfo.Size()/2, options...)
 	c.Assert(err, NotNil)
 
-	forceDeleteBucket(client, bucketName, c)
+	ForceDeleteBucket(client, bucketName, c)
 }
 
 // TestUploadFileWithSequential
@@ -707,7 +707,7 @@ func (s *OssUploadSuite) TestUploadFileWithSequential(c *C) {
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
 
-	bucketName := bucketNamePrefix + randLowStr(6)
+	bucketName := bucketNamePrefix + RandLowStr(6)
 	err = client.CreateBucket(bucketName)
 	c.Assert(err, IsNil)
 	bucket, err := client.Bucket(bucketName)
@@ -716,7 +716,7 @@ func (s *OssUploadSuite) TestUploadFileWithSequential(c *C) {
 	fileInfo, err := os.Stat(fileName)
 	c.Assert(err, IsNil)
 
-	objectName := objectNamePrefix + randStr(8)
+	objectName := objectNamePrefix + RandStr(8)
 
 	var respHeader http.Header
 
@@ -737,5 +737,5 @@ func (s *OssUploadSuite) TestUploadFileWithSequential(c *C) {
 	strMD5 := respHeader.Get("Content-MD5")
 	c.Assert(len(strMD5) > 0, Equals, true)
 
-	forceDeleteBucket(client, bucketName, c)
+	ForceDeleteBucket(client, bucketName, c)
 }

+ 146 - 51
oss/utils.go

@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"hash/crc32"
 	"hash/crc64"
+	"io"
 	"net/http"
 	"os"
 	"os/exec"
@@ -47,21 +48,42 @@ func getSysInfo() sysInfo {
 	return sysInfo{name: name, release: release, machine: machine}
 }
 
-// unpackedRange
-type unpackedRange struct {
-	hasStart bool  // Flag indicates if the start point is specified
-	hasEnd   bool  // Flag indicates if the end point is specified
-	start    int64 // Start point
-	end      int64 // End point
+// GetRangeConfig gets the download range from the options.
+func GetRangeConfig(options []Option) (*UnpackedRange, error) {
+	rangeOpt, err := FindOption(options, HTTPHeaderRange, nil)
+	if err != nil || rangeOpt == nil {
+		return nil, err
+	}
+	return ParseRange(rangeOpt.(string))
+}
+
+// UnpackedRange
+type UnpackedRange struct {
+	HasStart bool  // Flag indicates if the start point is specified
+	HasEnd   bool  // Flag indicates if the end point is specified
+	Start    int64 // Start point
+	End      int64 // End point
 }
 
-// invalidRangeError returns invalid range error
-func invalidRangeError(r string) error {
+// InvalidRangeError returns invalid range error
+func InvalidRangeError(r string) error {
 	return fmt.Errorf("InvalidRange %s", r)
 }
 
-// parseRange parse various styles of range such as bytes=M-N
-func parseRange(normalizedRange string) (*unpackedRange, error) {
+func GetRangeString(unpackRange UnpackedRange) string {
+	var strRange string
+	if unpackRange.HasStart && unpackRange.HasEnd {
+		strRange = fmt.Sprintf("%d-%d", unpackRange.Start, unpackRange.End)
+	} else if unpackRange.HasStart {
+		strRange = fmt.Sprintf("%d-", unpackRange.Start)
+	} else if unpackRange.HasEnd {
+		strRange = fmt.Sprintf("-%d", unpackRange.End)
+	}
+	return strRange
+}
+
+// ParseRange parse various styles of range such as bytes=M-N
+func ParseRange(normalizedRange string) (*UnpackedRange, error) {
 	var err error
 	hasStart := false
 	hasEnd := false
@@ -71,7 +93,7 @@ func parseRange(normalizedRange string) (*unpackedRange, error) {
 	// Bytes==M-N or ranges=M-N
 	nrSlice := strings.Split(normalizedRange, "=")
 	if len(nrSlice) != 2 || nrSlice[0] != "bytes" {
-		return nil, invalidRangeError(normalizedRange)
+		return nil, InvalidRangeError(normalizedRange)
 	}
 
 	// Bytes=M-N,X-Y
@@ -82,62 +104,62 @@ func parseRange(normalizedRange string) (*unpackedRange, error) {
 		startStr := rStr[:len(rStr)-1]
 		start, err = strconv.ParseInt(startStr, 10, 64)
 		if err != nil {
-			return nil, invalidRangeError(normalizedRange)
+			return nil, InvalidRangeError(normalizedRange)
 		}
 		hasStart = true
 	} else if strings.HasPrefix(rStr, "-") { // -N
 		len := rStr[1:]
 		end, err = strconv.ParseInt(len, 10, 64)
 		if err != nil {
-			return nil, invalidRangeError(normalizedRange)
+			return nil, InvalidRangeError(normalizedRange)
 		}
 		if end == 0 { // -0
-			return nil, invalidRangeError(normalizedRange)
+			return nil, InvalidRangeError(normalizedRange)
 		}
 		hasEnd = true
 	} else { // M-N
 		valSlice := strings.Split(rStr, "-")
 		if len(valSlice) != 2 {
-			return nil, invalidRangeError(normalizedRange)
+			return nil, InvalidRangeError(normalizedRange)
 		}
 		start, err = strconv.ParseInt(valSlice[0], 10, 64)
 		if err != nil {
-			return nil, invalidRangeError(normalizedRange)
+			return nil, InvalidRangeError(normalizedRange)
 		}
 		hasStart = true
 		end, err = strconv.ParseInt(valSlice[1], 10, 64)
 		if err != nil {
-			return nil, invalidRangeError(normalizedRange)
+			return nil, InvalidRangeError(normalizedRange)
 		}
 		hasEnd = true
 	}
 
-	return &unpackedRange{hasStart, hasEnd, start, end}, nil
+	return &UnpackedRange{hasStart, hasEnd, start, end}, nil
 }
 
-// adjustRange returns adjusted range, adjust the range according to the length of the file
-func adjustRange(ur *unpackedRange, size int64) (start, end int64) {
+// AdjustRange returns adjusted range, adjust the range according to the length of the file
+func AdjustRange(ur *UnpackedRange, size int64) (start, end int64) {
 	if ur == nil {
 		return 0, size
 	}
 
-	if ur.hasStart && ur.hasEnd {
-		start = ur.start
-		end = ur.end + 1
-		if ur.start < 0 || ur.start >= size || ur.end > size || ur.start > ur.end {
+	if ur.HasStart && ur.HasEnd {
+		start = ur.Start
+		end = ur.End + 1
+		if ur.Start < 0 || ur.Start >= size || ur.End > size || ur.Start > ur.End {
 			start = 0
 			end = size
 		}
-	} else if ur.hasStart {
-		start = ur.start
+	} else if ur.HasStart {
+		start = ur.Start
 		end = size
-		if ur.start < 0 || ur.start >= size {
+		if ur.Start < 0 || ur.Start >= size {
 			start = 0
 		}
-	} else if ur.hasEnd {
-		start = size - ur.end
+	} else if ur.HasEnd {
+		start = size - ur.End
 		end = size
-		if ur.end < 0 || ur.end > size {
+		if ur.End < 0 || ur.End > size {
 			start = 0
 			end = size
 		}
@@ -260,12 +282,12 @@ func GetPartEnd(begin int64, total int64, per int64) int64 {
 	return begin + per - 1
 }
 
-// crcTable returns the table constructed from the specified polynomial
-var crcTable = func() *crc64.Table {
+// CrcTable returns the table constructed from the specified polynomial
+var CrcTable = func() *crc64.Table {
 	return crc64.MakeTable(crc64.ECMA)
 }
 
-// crcTable returns the table constructed from the specified polynomial
+// CrcTable returns the table constructed from the specified polynomial
 var crc32Table = func() *crc32.Table {
 	return crc32.MakeTable(crc32.IEEE)
 }
@@ -274,28 +296,28 @@ var crc32Table = func() *crc32.Table {
 func ChoiceTransferPartOption(options []Option) []Option {
 	var outOption []Option
 
-	listener, _ := findOption(options, progressListener, nil)
+	listener, _ := FindOption(options, progressListener, nil)
 	if listener != nil {
 		outOption = append(outOption, Progress(listener.(ProgressListener)))
 	}
 
-	payer, _ := findOption(options, HTTPHeaderOssRequester, nil)
+	payer, _ := FindOption(options, HTTPHeaderOssRequester, nil)
 	if payer != nil {
 		outOption = append(outOption, RequestPayer(PayerType(payer.(string))))
 	}
 
-	versionId, _ := findOption(options, "versionId", nil)
+	versionId, _ := FindOption(options, "versionId", nil)
 	if versionId != nil {
 		outOption = append(outOption, VersionId(versionId.(string)))
 	}
 
-	trafficLimit, _ := findOption(options, HTTPHeaderOssTrafficLimit, nil)
+	trafficLimit, _ := FindOption(options, HTTPHeaderOssTrafficLimit, nil)
 	if trafficLimit != nil {
 		speed, _ := strconv.ParseInt(trafficLimit.(string), 10, 64)
 		outOption = append(outOption, TrafficLimitHeader(speed))
 	}
 
-	respHeader, _ := findOption(options, responseHeader, nil)
+	respHeader, _ := FindOption(options, responseHeader, nil)
 	if respHeader != nil {
 		outOption = append(outOption, GetResponseHeader(respHeader.(*http.Header)))
 	}
@@ -307,37 +329,37 @@ func ChoiceTransferPartOption(options []Option) []Option {
 func ChoiceCompletePartOption(options []Option) []Option {
 	var outOption []Option
 
-	listener, _ := findOption(options, progressListener, nil)
+	listener, _ := FindOption(options, progressListener, nil)
 	if listener != nil {
 		outOption = append(outOption, Progress(listener.(ProgressListener)))
 	}
 
-	payer, _ := findOption(options, HTTPHeaderOssRequester, nil)
+	payer, _ := FindOption(options, HTTPHeaderOssRequester, nil)
 	if payer != nil {
 		outOption = append(outOption, RequestPayer(PayerType(payer.(string))))
 	}
 
-	acl, _ := findOption(options, HTTPHeaderOssObjectACL, nil)
+	acl, _ := FindOption(options, HTTPHeaderOssObjectACL, nil)
 	if acl != nil {
 		outOption = append(outOption, ObjectACL(ACLType(acl.(string))))
 	}
 
-	callback, _ := findOption(options, HTTPHeaderOssCallback, nil)
+	callback, _ := FindOption(options, HTTPHeaderOssCallback, nil)
 	if callback != nil {
 		outOption = append(outOption, Callback(callback.(string)))
 	}
 
-	callbackVar, _ := findOption(options, HTTPHeaderOssCallbackVar, nil)
+	callbackVar, _ := FindOption(options, HTTPHeaderOssCallbackVar, nil)
 	if callbackVar != nil {
 		outOption = append(outOption, CallbackVar(callbackVar.(string)))
 	}
 
-	respHeader, _ := findOption(options, responseHeader, nil)
+	respHeader, _ := FindOption(options, responseHeader, nil)
 	if respHeader != nil {
 		outOption = append(outOption, GetResponseHeader(respHeader.(*http.Header)))
 	}
 
-	forbidOverWrite, _ := findOption(options, HTTPHeaderOssForbidOverWrite, nil)
+	forbidOverWrite, _ := FindOption(options, HTTPHeaderOssForbidOverWrite, nil)
 	if forbidOverWrite != nil {
 		if forbidOverWrite.(string) == "true" {
 			outOption = append(outOption, ForbidOverWrite(true))
@@ -352,12 +374,12 @@ func ChoiceCompletePartOption(options []Option) []Option {
 // ChoiceAbortPartOption choices valid option supported by AbortMultipartUpload
 func ChoiceAbortPartOption(options []Option) []Option {
 	var outOption []Option
-	payer, _ := findOption(options, HTTPHeaderOssRequester, nil)
+	payer, _ := FindOption(options, HTTPHeaderOssRequester, nil)
 	if payer != nil {
 		outOption = append(outOption, RequestPayer(PayerType(payer.(string))))
 	}
 
-	respHeader, _ := findOption(options, responseHeader, nil)
+	respHeader, _ := FindOption(options, responseHeader, nil)
 	if respHeader != nil {
 		outOption = append(outOption, GetResponseHeader(respHeader.(*http.Header)))
 	}
@@ -370,17 +392,17 @@ func ChoiceHeadObjectOption(options []Option) []Option {
 	var outOption []Option
 
 	// not select HTTPHeaderRange to get whole object length
-	payer, _ := findOption(options, HTTPHeaderOssRequester, nil)
+	payer, _ := FindOption(options, HTTPHeaderOssRequester, nil)
 	if payer != nil {
 		outOption = append(outOption, RequestPayer(PayerType(payer.(string))))
 	}
 
-	versionId, _ := findOption(options, "versionId", nil)
+	versionId, _ := FindOption(options, "versionId", nil)
 	if versionId != nil {
 		outOption = append(outOption, VersionId(versionId.(string)))
 	}
 
-	respHeader, _ := findOption(options, responseHeader, nil)
+	respHeader, _ := FindOption(options, responseHeader, nil)
 	if respHeader != nil {
 		outOption = append(outOption, GetResponseHeader(respHeader.(*http.Header)))
 	}
@@ -399,11 +421,84 @@ func CheckBucketName(bucketName string) error {
 			return fmt.Errorf("bucket name %s can only include lowercase letters, numbers, and -", bucketName)
 		}
 	}
-
 	if bucketName[0] == '-' || bucketName[nameLen-1] == '-' {
 		return fmt.Errorf("bucket name %s must start and end with a lowercase letter or number", bucketName)
 	}
+	return nil
+}
+
+func GetReaderLen(reader io.Reader) (int64, error) {
+	var contentLength int64
+	var err error
+	switch v := reader.(type) {
+	case *bytes.Buffer:
+		contentLength = int64(v.Len())
+	case *bytes.Reader:
+		contentLength = int64(v.Len())
+	case *strings.Reader:
+		contentLength = int64(v.Len())
+	case *os.File:
+		fInfo, fError := v.Stat()
+		if fError != nil {
+			err = fmt.Errorf("can't get reader content length,%s", fError.Error())
+		} else {
+			contentLength = fInfo.Size()
+		}
+	case *io.LimitedReader:
+		contentLength = int64(v.N)
+	case *LimitedReadCloser:
+		contentLength = int64(v.N)
+	default:
+		err = fmt.Errorf("can't get reader content length,unkown reader type")
+	}
+	return contentLength, err
+}
 
+func LimitReadCloser(r io.Reader, n int64) io.Reader {
+	var lc LimitedReadCloser
+	lc.R = r
+	lc.N = n
+	return &lc
+}
+
+// LimitedRC support Close()
+type LimitedReadCloser struct {
+	io.LimitedReader
+}
+
+func (lc *LimitedReadCloser) Close() error {
+	if closer, ok := lc.R.(io.ReadCloser); ok {
+		return closer.Close()
+	}
 	return nil
+}
+
+type DiscardReadCloser struct {
+	RC      io.ReadCloser
+	Discard int
+}
 
+func (drc *DiscardReadCloser) Read(b []byte) (int, error) {
+	n, err := drc.RC.Read(b)
+	if drc.Discard == 0 || n <= 0 {
+		return n, err
+	}
+
+	if n <= drc.Discard {
+		drc.Discard -= n
+		return 0, err
+	}
+
+	realLen := n - drc.Discard
+	copy(b[0:realLen], b[drc.Discard:n])
+	drc.Discard = 0
+	return realLen, err
+}
+
+func (drc *DiscardReadCloser) Close() error {
+	closer, ok := drc.RC.(io.ReadCloser)
+	if ok {
+		return closer.Close()
+	}
+	return nil
 }

+ 42 - 42
oss/utils_test.go

@@ -85,7 +85,7 @@ func (s *OssUtilsSuite) TestUtilsSplitFile(c *C) {
 }
 
 func (s *OssUtilsSuite) TestUtilsFileExt(c *C) {
-	c.Assert(strings.Contains(TypeByExtension("test.txt"), "text/plain; charset=utf-8"), Equals, true)
+	c.Assert(strings.Contains(TypeByExtension("test.txt"), "text/plain"), Equals, true)
 	c.Assert(TypeByExtension("test.jpg"), Equals, "image/jpeg")
 	c.Assert(TypeByExtension("test.pdf"), Equals, "application/pdf")
 	c.Assert(TypeByExtension("test"), Equals, "")
@@ -108,117 +108,117 @@ func (s *OssUtilsSuite) TestGetPartEnd(c *C) {
 
 func (s *OssUtilsSuite) TestParseRange(c *C) {
 	// InvalidRange bytes==M-N
-	_, err := parseRange("bytes==M-N")
+	_, err := ParseRange("bytes==M-N")
 	c.Assert(err, NotNil)
 	c.Assert(err.Error(), Equals, "InvalidRange bytes==M-N")
 
 	// InvalidRange ranges=M-N
-	_, err = parseRange("ranges=M-N")
+	_, err = ParseRange("ranges=M-N")
 	c.Assert(err, NotNil)
 	c.Assert(err.Error(), Equals, "InvalidRange ranges=M-N")
 
 	// InvalidRange ranges=M-N
-	_, err = parseRange("bytes=M-N")
+	_, err = ParseRange("bytes=M-N")
 	c.Assert(err, NotNil)
 	c.Assert(err.Error(), Equals, "InvalidRange bytes=M-N")
 
 	// InvalidRange ranges=M-
-	_, err = parseRange("bytes=M-")
+	_, err = ParseRange("bytes=M-")
 	c.Assert(err, NotNil)
 	c.Assert(err.Error(), Equals, "InvalidRange bytes=M-")
 
 	// InvalidRange ranges=-N
-	_, err = parseRange("bytes=-N")
+	_, err = ParseRange("bytes=-N")
 	c.Assert(err, NotNil)
 	c.Assert(err.Error(), Equals, "InvalidRange bytes=-N")
 
 	// InvalidRange ranges=-0
-	_, err = parseRange("bytes=-0")
+	_, err = ParseRange("bytes=-0")
 	c.Assert(err, NotNil)
 	c.Assert(err.Error(), Equals, "InvalidRange bytes=-0")
 
 	// InvalidRange bytes=1-2-3
-	_, err = parseRange("bytes=1-2-3")
+	_, err = ParseRange("bytes=1-2-3")
 	c.Assert(err, NotNil)
 	c.Assert(err.Error(), Equals, "InvalidRange bytes=1-2-3")
 
 	// InvalidRange bytes=1-N
-	_, err = parseRange("bytes=1-N")
+	_, err = ParseRange("bytes=1-N")
 	c.Assert(err, NotNil)
 	c.Assert(err.Error(), Equals, "InvalidRange bytes=1-N")
 
 	// Ranges=M-N
-	ur, err := parseRange("bytes=1024-4096")
+	ur, err := ParseRange("bytes=1024-4096")
 	c.Assert(err, IsNil)
-	c.Assert(ur.start, Equals, (int64)(1024))
-	c.Assert(ur.end, Equals, (int64)(4096))
-	c.Assert(ur.hasStart, Equals, true)
-	c.Assert(ur.hasEnd, Equals, true)
+	c.Assert(ur.Start, Equals, (int64)(1024))
+	c.Assert(ur.End, Equals, (int64)(4096))
+	c.Assert(ur.HasStart, Equals, true)
+	c.Assert(ur.HasEnd, Equals, true)
 
 	// Ranges=M-N,X-Y
-	ur, err = parseRange("bytes=1024-4096,2048-4096")
+	ur, err = ParseRange("bytes=1024-4096,2048-4096")
 	c.Assert(err, IsNil)
-	c.Assert(ur.start, Equals, (int64)(1024))
-	c.Assert(ur.end, Equals, (int64)(4096))
-	c.Assert(ur.hasStart, Equals, true)
-	c.Assert(ur.hasEnd, Equals, true)
+	c.Assert(ur.Start, Equals, (int64)(1024))
+	c.Assert(ur.End, Equals, (int64)(4096))
+	c.Assert(ur.HasStart, Equals, true)
+	c.Assert(ur.HasEnd, Equals, true)
 
 	// Ranges=M-
-	ur, err = parseRange("bytes=1024-")
+	ur, err = ParseRange("bytes=1024-")
 	c.Assert(err, IsNil)
-	c.Assert(ur.start, Equals, (int64)(1024))
-	c.Assert(ur.end, Equals, (int64)(0))
-	c.Assert(ur.hasStart, Equals, true)
-	c.Assert(ur.hasEnd, Equals, false)
+	c.Assert(ur.Start, Equals, (int64)(1024))
+	c.Assert(ur.End, Equals, (int64)(0))
+	c.Assert(ur.HasStart, Equals, true)
+	c.Assert(ur.HasEnd, Equals, false)
 
 	// Ranges=-N
-	ur, err = parseRange("bytes=-4096")
+	ur, err = ParseRange("bytes=-4096")
 	c.Assert(err, IsNil)
-	c.Assert(ur.start, Equals, (int64)(0))
-	c.Assert(ur.end, Equals, (int64)(4096))
-	c.Assert(ur.hasStart, Equals, false)
-	c.Assert(ur.hasEnd, Equals, true)
+	c.Assert(ur.Start, Equals, (int64)(0))
+	c.Assert(ur.End, Equals, (int64)(4096))
+	c.Assert(ur.HasStart, Equals, false)
+	c.Assert(ur.HasEnd, Equals, true)
 }
 
 func (s *OssUtilsSuite) TestAdjustRange(c *C) {
 	// Nil
-	start, end := adjustRange(nil, 8192)
+	start, end := AdjustRange(nil, 8192)
 	c.Assert(start, Equals, (int64)(0))
 	c.Assert(end, Equals, (int64)(8192))
 
 	// 1024-4096
-	ur := &unpackedRange{true, true, 1024, 4095}
-	start, end = adjustRange(ur, 8192)
+	ur := &UnpackedRange{true, true, 1024, 4095}
+	start, end = AdjustRange(ur, 8192)
 	c.Assert(start, Equals, (int64)(1024))
 	c.Assert(end, Equals, (int64)(4096))
 
 	// 1024-
-	ur = &unpackedRange{true, false, 1024, 4096}
-	start, end = adjustRange(ur, 8192)
+	ur = &UnpackedRange{true, false, 1024, 4096}
+	start, end = AdjustRange(ur, 8192)
 	c.Assert(start, Equals, (int64)(1024))
 	c.Assert(end, Equals, (int64)(8192))
 
 	// -4096
-	ur = &unpackedRange{false, true, 1024, 4096}
-	start, end = adjustRange(ur, 8192)
+	ur = &UnpackedRange{false, true, 1024, 4096}
+	start, end = AdjustRange(ur, 8192)
 	c.Assert(start, Equals, (int64)(4096))
 	c.Assert(end, Equals, (int64)(8192))
 
 	// Invalid range 4096-1024
-	ur = &unpackedRange{true, true, 4096, 1024}
-	start, end = adjustRange(ur, 8192)
+	ur = &UnpackedRange{true, true, 4096, 1024}
+	start, end = AdjustRange(ur, 8192)
 	c.Assert(start, Equals, (int64)(0))
 	c.Assert(end, Equals, (int64)(8192))
 
 	// Invalid range -1-
-	ur = &unpackedRange{true, false, -1, 0}
-	start, end = adjustRange(ur, 8192)
+	ur = &UnpackedRange{true, false, -1, 0}
+	start, end = AdjustRange(ur, 8192)
 	c.Assert(start, Equals, (int64)(0))
 	c.Assert(end, Equals, (int64)(8192))
 
 	// Invalid range -9999
-	ur = &unpackedRange{false, true, 0, 9999}
-	start, end = adjustRange(ur, 8192)
+	ur = &UnpackedRange{false, true, 0, 9999}
+	start, end = AdjustRange(ur, 8192)
 	c.Assert(start, Equals, (int64)(0))
 	c.Assert(end, Equals, (int64)(8192))
 }

+ 1 - 1
sample/append_object.go

@@ -101,7 +101,7 @@ func AppendObjectSample() {
 
 	// Case 4: Get the next append position by GetObjectDetailedMeta
 	props, err := bucket.GetObjectDetailedMeta(objectKey)
-	nextPos, err = strconv.ParseInt(props.Get(oss.HTTPHeaderOssNextAppendPosition), 10, 0)
+	nextPos, err = strconv.ParseInt(props.Get(oss.HTTPHeaderOssNextAppendPosition), 10, 64)
 	if err != nil {
 		HandleError(err)
 	}

BIN
sample/test-client-encryption-crypto-cpp-rsa.jpg


BIN
sample/test-client-encryption-crypto-python-rsa.jpg


BIN
sample/test-client-encryption-src.jpg


+ 304 - 0
sample_crypto/sample_crypto.go

@@ -0,0 +1,304 @@
+package main
+
+import (
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"os"
+
+	kms "github.com/aliyun/alibaba-cloud-sdk-go/services/kms"
+	"github.com/aliyun/aliyun-oss-go-sdk/oss"
+	"github.com/aliyun/aliyun-oss-go-sdk/oss/crypto"
+)
+
+func SampleRsaNormalObject() {
+	// create oss client
+	client, err := oss.New("<yourEndpoint>", "<yourAccessKeyId>", "<yourAccessKeySecret>")
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+
+	// Create a description of the master key. Once created, it cannot be modified. The master key description and the master key are one-to-one correspondence.
+	// If all objects use the same master key, the master key description can also be empty, but subsequent replacement of the master key is not supported.
+	// Because if the description is empty, it is impossible to determine which master key is used when decrypting object.
+	// It is strongly recommended that: configure the master key description(json string) for each master key, and the client should save the correspondence between them.
+	// The server does not save their correspondence
+
+	// Map converted by the master key description information (json string)
+	materialDesc := make(map[string]string)
+	materialDesc["desc"] = "<your master encrypt key material describe information>"
+
+	// Create a master key object based on the master key description
+	masterRsaCipher, err := osscrypto.CreateMasterRsa(materialDesc, "<your rsa public key>", "<your rsa private key>")
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+
+	// Create an interface for encryption based on the master key object, encrypt using aec ctr mode
+	contentProvider := osscrypto.CreateAesCtrCipher(masterRsaCipher)
+
+	// Get a storage space for client encryption, the bucket has to be created
+	// Client-side encrypted buckets have similar usages to ordinary buckets.
+	cryptoBucket, err := osscrypto.GetCryptoBucket(client, "<yourBucketName>", contentProvider)
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+
+	// put object ,will be automatically encrypted
+	err = cryptoBucket.PutObject("<yourObjectName>", bytes.NewReader([]byte("yourObjectValueByteArrary")))
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+
+	// get object ,will be automatically decrypted
+	body, err := cryptoBucket.GetObject("<yourObjectName>")
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+	defer body.Close()
+
+	data, err := ioutil.ReadAll(body)
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+	fmt.Println("data:", string(data))
+}
+
+func SampleRsaMultiPartObject() {
+	// create oss client
+	client, err := oss.New("<yourEndpoint>", "<yourAccessKeyId>", "<yourAccessKeySecret>")
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+
+	// Create a description of the master key. Once created, it cannot be modified. The master key description and the master key are one-to-one correspondence.
+	// If all objects use the same master key, the master key description can also be empty, but subsequent replacement of the master key is not supported.
+	// Because if the description is empty, it is impossible to determine which master key is used when decrypting object.
+	// It is strongly recommended that: configure the master key description(json string) for each master key, and the client should save the correspondence between them.
+	// The server does not save their correspondence
+
+	// Map converted by the master key description information (json string)
+	materialDesc := make(map[string]string)
+	materialDesc["desc"] = "<your master encrypt key material describe information>"
+
+	// Create a master key object based on the master key description
+	masterRsaCipher, err := osscrypto.CreateMasterRsa(materialDesc, "<your rsa public key>", "<your rsa private key>")
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+
+	// Create an interface for encryption based on the master key object, encrypt using aec ctr mode
+	contentProvider := osscrypto.CreateAesCtrCipher(masterRsaCipher)
+
+	// Get a storage space for client encryption, the bucket has to be created
+	// Client-side encrypted buckets have similar usages to ordinary buckets.
+	cryptoBucket, err := osscrypto.GetCryptoBucket(client, "<yourBucketName>", contentProvider)
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+
+	fileName := "<yourLocalFilePath>"
+	fileInfo, err := os.Stat(fileName)
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+	fileSize := fileInfo.Size()
+
+	// Encryption context information
+	var cryptoContext osscrypto.PartCryptoContext
+	cryptoContext.DataSize = fileSize
+
+	// The expected number of parts, the actual number of parts is subject to subsequent calculations.
+	expectPartCount := int64(10)
+
+	//Currently aes ctr encryption block size requires 16 byte alignment
+	cryptoContext.PartSize = (fileSize / expectPartCount / 16) * 16
+
+	imur, err := cryptoBucket.InitiateMultipartUpload("<yourObjectName>", &cryptoContext)
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+
+	chunks, err := oss.SplitFileByPartSize(fileName, cryptoContext.PartSize)
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+
+	var partsUpload []oss.UploadPart
+	for _, chunk := range chunks {
+		part, err := cryptoBucket.UploadPartFromFile(imur, fileName, chunk.Offset, chunk.Size, (int)(chunk.Number), cryptoContext)
+		if err != nil {
+			fmt.Println("Error:", err)
+			os.Exit(-1)
+		}
+		partsUpload = append(partsUpload, part)
+	}
+
+	// Complete
+	_, err = cryptoBucket.CompleteMultipartUpload(imur, partsUpload)
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+}
+
+// Query the master key according to the master key description information.
+// If you need to decrypt different master key encryption objects, you need to provide this interface.
+type MockRsaManager struct {
+}
+
+func (mg *MockRsaManager) GetMasterKey(matDesc map[string]string) ([]string, error) {
+	// to do
+	keyList := []string{"<yourRsaPublicKey>", "<yourRsaPrivatKey>"}
+	return keyList, nil
+}
+
+// Decrypt the object encrypted by different master keys
+func SampleMultipleMasterRsa() {
+	// create oss client
+	client, err := oss.New("<yourEndpoint>", "<yourAccessKeyId>", "<yourAccessKeySecret>")
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+
+	// Create a description of the master key. Once created, it cannot be modified. The master key description and the master key are one-to-one correspondence.
+	// If all objects use the same master key, the master key description can also be empty, but subsequent replacement of the master key is not supported.
+	// Because if the description is empty, it is impossible to determine which master key is used when decrypting object.
+	// It is strongly recommended that: configure the master key description(json string) for each master key, and the client should save the correspondence between them.
+	// The server does not save their correspondence
+
+	// Map converted by the master key description information (json string)
+	materialDesc := make(map[string]string)
+	materialDesc["desc"] = "<your master encrypt key material describe information>"
+
+	// Create a master key object based on the master key description
+	masterRsaCipher, err := osscrypto.CreateMasterRsa(materialDesc, "<your rsa public key>", "<your rsa private key>")
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+
+	// Create an interface for encryption based on the master key object, encrypt using aec ctr mode
+	contentProvider := osscrypto.CreateAesCtrCipher(masterRsaCipher)
+
+	// If you need to decrypt objects encrypted by different ma keys, you need to provide this interface.
+	var mockRsaManager MockRsaManager
+	var options []osscrypto.CryptoBucketOption
+	options = append(options, osscrypto.SetMasterCipherManager(&mockRsaManager))
+
+	// Get a storage space for client encryption, the bucket has to be created
+	// Client-side encrypted buckets have similar usages to ordinary buckets.
+	cryptoBucket, err := osscrypto.GetCryptoBucket(client, "<yourBucketName>", contentProvider, options...)
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+
+	// put object ,will be automatically encrypted
+	err = cryptoBucket.PutObject("<yourObjectName>", bytes.NewReader([]byte("yourObjectValueByteArrary")))
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+
+	// get object ,will be automatically decrypted
+	body, err := cryptoBucket.GetObject("<otherObjectNameEncryptedWithOtherRsa>")
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+	defer body.Close()
+
+	data, err := ioutil.ReadAll(body)
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+	fmt.Println("data:", string(data))
+}
+
+func SampleKmsNormalObject() {
+	// create oss client
+	client, err := oss.New("<yourEndpoint>", "<yourAccessKeyId>", "<yourAccessKeySecret>")
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+
+	// create kms client
+	kmsClient, err := kms.NewClientWithAccessKey("<yourKmsRegion>", "<yourKmsAccessKeyId>", "<yourKmsAccessKeySecret>")
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+
+	// Create a description of the master key. Once created, it cannot be modified. The master key description and the master key are one-to-one correspondence.
+	// If all objects use the same master key, the master key description can also be empty, but subsequent replacement of the master key is not supported.
+	// Because if the description is empty, it is impossible to determine which master key is used when decrypting object.
+	// It is strongly recommended that: configure the master key description(json string) for each master key, and the client should save the correspondence between them.
+	// The server does not save their correspondence
+
+	// Map converted by the master key description information (json string)
+	materialDesc := make(map[string]string)
+	materialDesc["desc"] = "<your kms encrypt key material describe information>"
+
+	// Create a master key object based on the master key description
+	masterkmsCipher, err := osscrypto.CreateMasterAliKms(materialDesc, "<YourKmsId>", kmsClient)
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+
+	// Create an interface for encryption based on the master key object, encrypt using aec ctr mode
+	contentProvider := osscrypto.CreateAesCtrCipher(masterkmsCipher)
+
+	// Get a storage space for client encryption, the bucket has to be created
+	// Client-side encrypted buckets have similar usages to ordinary buckets.
+	cryptoBucket, err := osscrypto.GetCryptoBucket(client, "<yourBucketName>", contentProvider)
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+
+	// put object ,will be automatically encrypted
+	err = cryptoBucket.PutObject("<yourObjectName>", bytes.NewReader([]byte("yourObjectValueByteArrary")))
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+
+	// get object ,will be automatically decrypted
+	body, err := cryptoBucket.GetObject("<yourObjectName>")
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+	defer body.Close()
+
+	data, err := ioutil.ReadAll(body)
+	if err != nil {
+		fmt.Println("Error:", err)
+		os.Exit(-1)
+	}
+	fmt.Println("data:", string(data))
+}
+
+func main() {
+	SampleRsaNormalObject()
+	SampleRsaMultiPartObject()
+	SampleMultipleMasterRsa()
+	SampleKmsNormalObject()
+}

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor