鸣镝 9 anni fa
parent
commit
35643fc1f1
9 ha cambiato i file con 351 aggiunte e 248 eliminazioni
  1. 131 118
      oss/bucket.go
  2. 42 42
      oss/client.go
  3. 6 0
      oss/client_test.go
  4. 11 20
      oss/conn.go
  5. 38 28
      oss/crc_test.go
  6. 2 2
      oss/error.go
  7. 60 0
      oss/model.go
  8. 55 38
      oss/multipart.go
  9. 6 0
      oss/option.go

+ 131 - 118
oss/bucket.go

@@ -5,7 +5,6 @@ import (
 	"crypto/md5"
 	"encoding/base64"
 	"encoding/xml"
-	"hash"
 	"hash/crc64"
 	"io"
 	"io/ioutil"
@@ -34,20 +33,18 @@ type Bucket struct {
 //
 func (bucket Bucket) PutObject(objectKey string, reader io.Reader, options ...Option) error {
 	opts := addContentType(options, objectKey)
-	resp, err := bucket.do("PUT", objectKey, "", "", opts, reader)
+
+	request := &PutObjectRequest{
+		ObjectKey: objectKey,
+		Reader:    reader,
+	}
+	resp, err := bucket.DoPutObject(request, opts)
 	if err != nil {
 		return err
 	}
-	defer resp.body.Close()
-
-	if bucket.getConfig().IsEnableCRC {
-		err = checkCRC(resp, "PutObject")
-		if err != nil {
-			return err
-		}
-	}
+	defer resp.Body.Close()
 
-	return checkRespCode(resp.statusCode, []int{http.StatusOK})
+	return err
 }
 
 //
@@ -68,68 +65,68 @@ func (bucket Bucket) PutObjectFromFile(objectKey, filePath string, options ...Op
 
 	opts := addContentType(options, filePath, objectKey)
 
-	resp, err := bucket.do("PUT", objectKey, "", "", opts, fd)
+	request := &PutObjectRequest{
+		ObjectKey: objectKey,
+		Reader:    fd,
+	}
+	resp, err := bucket.DoPutObject(request, opts)
 	if err != nil {
 		return err
 	}
-	defer resp.body.Close()
+	defer resp.Body.Close()
 
-	if bucket.getConfig().IsEnableCRC {
-		err = checkCRC(resp, "PutObjectFromFile")
-		if err != nil {
-			return err
-		}
-	}
-
-	return checkRespCode(resp.statusCode, []int{http.StatusOK})
+	return err
 }
 
 //
-// GetObject 下载文件。
+// DoPutObject 上传文件。
 //
-// objectKey 下载的文件名称。
-// options   对象的属性限制项,可选值有Range、IfModifiedSince、IfUnmodifiedSince、IfMatch、
-// IfNoneMatch、AcceptEncoding,详细请参考
-// https://help.aliyun.com/document_detail/oss/api-reference/object/GetObject.html
+// request  上传请求。
+// options  上传选项。
 //
-// io.ReadCloser  reader,读取数据后需要close。error为nil时有效
+// Response 上传请求返回值。
 // error  操作无错误为nil,非nil为错误信息。
 //
-func (bucket Bucket) GetObject(objectKey string, options ...Option) (io.ReadCloser, error) {
-	resp, err := bucket.do("GET", objectKey, "", "", options, nil)
+func (bucket Bucket) DoPutObject(request *PutObjectRequest, options []Option) (*Response, error) {
+	isOptSet, _, _ := isOptionSet(options, HTTPHeaderContentType)
+	if !isOptSet {
+		options = addContentType(options, request.ObjectKey)
+	}
+
+	resp, err := bucket.do("PUT", request.ObjectKey, "", "", options, request.Reader)
 	if err != nil {
 		return nil, err
 	}
-	return resp.body, nil
+
+	if bucket.getConfig().IsEnableCRC {
+		err = checkCRC(resp, "DoPutObject")
+		if err != nil {
+			return resp, err
+		}
+	}
+
+	err = checkRespCode(resp.StatusCode, []int{http.StatusOK})
+
+	return resp, err
 }
 
 //
-// GetObjectWithCRC 下载文件同时计算CRC。
+// GetObject 下载文件。
 //
 // objectKey 下载的文件名称。
 // options   对象的属性限制项,可选值有Range、IfModifiedSince、IfUnmodifiedSince、IfMatch、
 // IfNoneMatch、AcceptEncoding,详细请参考
 // https://help.aliyun.com/document_detail/oss/api-reference/object/GetObject.html
 //
-// io.ReadCloser  body,文件内容,读取数据后需要close。error为nil时有效。
-// hash.Hash64 clientCRCCalculator,读取数据的CRC计算器。数据读取介绍通过clientCRCCalculator.Sum64()获取。
-// int64 serverCRC 服务器端存储数据的CRC值。
+// io.ReadCloser  reader,读取数据后需要close。error为nil时有效。
 // error  操作无错误为nil,非nil为错误信息。
 //
-func (bucket Bucket) GetObjectWithCRC(objectKey string, options ...Option) (body io.ReadCloser, clientCRCCalculator hash.Hash64, serverCRC uint64, err error) {
-	resp, err := bucket.do("GET", objectKey, "", "", options, nil)
+func (bucket Bucket) GetObject(objectKey string, options ...Option) (io.ReadCloser, error) {
+	result, err := bucket.DoGetObject(&GetObjectRequest{objectKey}, options)
 	if err != nil {
-		return
-	}
-
-	body = resp.body
-	hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
-	if bucket.getConfig().IsEnableCRC && !hasRange {
-		serverCRC = resp.serverCRC
-		clientCRCCalculator = crc64.New(crcTable())
-		body = ioutil.NopCloser(io.TeeReader(body, clientCRCCalculator))
+		return nil, err
 	}
-	return
+	return result.Response.Body, nil
 }
 
 //
@@ -142,11 +139,12 @@ func (bucket Bucket) GetObjectWithCRC(objectKey string, options ...Option) (body
 // error  操作无错误时返回error为nil,非nil为错误说明。
 //
 func (bucket Bucket) GetObjectToFile(objectKey, filePath string, options ...Option) error {
-	resp, err := bucket.do("GET", objectKey, "", "", options, nil)
+	// 读取Object内容
+	result, err := bucket.DoGetObject(&GetObjectRequest{objectKey}, options)
 	if err != nil {
 		return err
 	}
-	defer resp.body.Close()
+	defer result.Response.Body.Close()
 
 	// 如果文件不存在则创建,存在则清空
 	fd, err := os.OpenFile(filePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0660)
@@ -155,24 +153,17 @@ func (bucket Bucket) GetObjectToFile(objectKey, filePath string, options ...Opti
 	}
 	defer fd.Close()
 
-	// 读取数据同时计算CRC
-	body := resp.body
-	var crc hash.Hash64
-	hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
-	if bucket.getConfig().IsEnableCRC && !hasRange {
-		crc = crc64.New(crcTable())
-		body = ioutil.NopCloser(io.TeeReader(body, crc))
-	}
-
-	_, err = io.Copy(fd, body)
+	// 存储数据到文件
+	_, err = io.Copy(fd, result.Response.Body)
 	if err != nil {
 		return err
 	}
 
 	// 比较CRC值
+	hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
 	if bucket.getConfig().IsEnableCRC && !hasRange {
-		resp.clientCRC = crc.Sum64()
-		err = checkCRC(resp, "GetObjectToFile")
+		result.Response.ClientCRC = result.ClientCRC.Sum64()
+		err = checkCRC(result.Response, "GetObjectToFile")
 		if err != nil {
 			return err
 		}
@@ -181,6 +172,36 @@ func (bucket Bucket) GetObjectToFile(objectKey, filePath string, options ...Opti
 	return nil
 }
 
+//
+// DoGetObject 下载文件
+//
+// request 下载请求
+// options    对象的属性限制项。详见GetObject的options。
+//
+// GetObjectResult 下载请求返回值。
+// error  操作无错误为nil,非nil为错误信息。
+//
+func (bucket Bucket) DoGetObject(request *GetObjectRequest, options []Option) (*GetObjectResult, error) {
+	resp, err := bucket.do("GET", request.ObjectKey, "", "", options, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	result := &GetObjectResult{
+		Response: resp,
+	}
+
+	hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
+	if bucket.getConfig().IsEnableCRC && !hasRange {
+		crcCalc := crc64.New(crcTable())
+		resp.Body = ioutil.NopCloser(io.TeeReader(resp.Body, crcCalc))
+		result.ServerCRC = resp.ServerCRC
+		result.ClientCRC = crcCalc
+	}
+
+	return result, nil
+}
+
 //
 // CopyObject 同一个bucket内拷贝Object。
 //
@@ -201,9 +222,9 @@ func (bucket Bucket) CopyObject(srcObjectKey, destObjectKey string, options ...O
 	if err != nil {
 		return out, err
 	}
-	defer resp.body.Close()
+	defer resp.Body.Close()
 
-	err = xmlUnmarshal(resp.body, &out)
+	err = xmlUnmarshal(resp.Body, &out)
 	return out, err
 }
 
@@ -254,9 +275,9 @@ func (bucket Bucket) copy(srcObjectKey, destBucketName, destObjectKey string, op
 	if err != nil {
 		return out, err
 	}
-	defer resp.body.Close()
+	defer resp.Body.Close()
 
-	err = xmlUnmarshal(resp.body, &out)
+	err = xmlUnmarshal(resp.Body, &out)
 	return out, err
 }
 
@@ -278,68 +299,60 @@ func (bucket Bucket) copy(srcObjectKey, destBucketName, destObjectKey string, op
 // error 操作无错误为nil,非nil为错误信息。
 //
 func (bucket Bucket) AppendObject(objectKey string, reader io.Reader, appendPosition int64, options ...Option) (int64, error) {
-	var nextAppendPosition int64
-	params := "append&position=" + strconv.Itoa(int(appendPosition))
-	opts := addContentType(options, objectKey)
-	resp, err := bucket.do("POST", objectKey, params, params, opts, reader)
-	if err != nil {
-		return appendPosition, err
+	request := &AppendObjectRequest{
+		ObjectKey: objectKey,
+		Reader:    reader,
+		Position:  appendPosition,
 	}
-	defer resp.body.Close()
 
-	nextAppendPosition, err = strconv.ParseInt(resp.headers.Get(HTTPHeaderOssNextAppendPosition), 10, 64)
-	if err != nil {
-		return nextAppendPosition, err
-	}
+	result, err := bucket.DoAppendObject(request, options)
 
-	return nextAppendPosition, nil
+	return result.NextPosition, err
 }
 
 //
-// AppendObjectWithCRC 追加方式上传。
-//
-// AppendObject参数必须包含position,其值指定从何处进行追加。首次追加操作的position必须为0,
-// 后续追加操作的position是Object的当前长度。例如,第一次Append Object请求指定position值为0,
-// content-length是65536;那么,第二次Append Object需要指定position为65536。
-// 每次操作成功后,响应头部x-oss-next-append-position也会标明下一次追加的position。
+// DoAppendObject 追加上传。
 //
-// objectKey  需要追加的Object。
-// reader     io.Reader,读取追的内容。
-// appendPosition  object追加的起始位置。
-// initCRC  上次追加的返回的CRC校验值,第一次填0。
-// destObjectProperties  第一次追加时指定新对象的属性,如CacheControl、ContentDisposition、ContentEncoding、
-// Expires、ServerSideEncryption、ObjectACL。
+// request 追加上传请求。
+// options 追加上传选项。
 //
-// int64 下次追加的开始位置,error为nil空时有效。
-// int64 本次追加后文件的CRC值,作为下次的初始化值。
-// error 操作无错误为nil,非nil为错误信息。
+// AppendObjectResult 追加上传请求返回值。
+// error  操作无错误为nil,非nil为错误信息。
 //
-func (bucket Bucket) AppendObjectWithCRC(objectKey string, reader io.Reader, appendPosition int64, initCRC uint64, options ...Option) (int64, uint64, error) {
-	var nextAppendPosition int64
-	params := "append&position=" + strconv.Itoa(int(appendPosition))
-	opts := addContentType(options, objectKey)
+func (bucket Bucket) DoAppendObject(request *AppendObjectRequest, options []Option) (*AppendObjectResult, error) {
+	params := "append&position=" + strconv.FormatInt(request.Position, 10)
 	headers := make(map[string]string)
 
+	opts := addContentType(options, request.ObjectKey)
 	handleOptions(headers, opts)
-	resp, err := bucket.Client.Conn.Do("POST", bucket.BucketName, objectKey, params, params, headers, reader, initCRC)
-	if err != nil {
-		return nextAppendPosition, initCRC, err
+
+	var initCRC uint64
+	isCRCSet, initCRCStr, _ := isOptionSet(options, initCRC64)
+	if isCRCSet {
+		initCRC, _ = strconv.ParseUint(initCRCStr, 10, 64)
 	}
-	defer resp.body.Close()
 
-	nextAppendPosition, err = strconv.ParseInt(resp.headers.Get(HTTPHeaderOssNextAppendPosition), 10, 64)
+	handleOptions(headers, opts)
+	resp, err := bucket.Client.Conn.Do("POST", bucket.BucketName, request.ObjectKey, params, params, headers, request.Reader, initCRC)
 	if err != nil {
-		return nextAppendPosition, initCRC, err
+		return nil, err
 	}
+	defer resp.Body.Close()
 
-	if bucket.getConfig().IsEnableCRC {
+	nextPosition, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderOssNextAppendPosition), 10, 64)
+	result := &AppendObjectResult{
+		NextPosition: nextPosition,
+		CRC:          resp.ServerCRC,
+	}
+
+	if bucket.getConfig().IsEnableCRC && isCRCSet {
 		err = checkCRC(resp, "AppendObject")
 		if err != nil {
-			return nextAppendPosition, resp.serverCRC, err
+			return result, err
 		}
 	}
 
-	return nextAppendPosition, resp.serverCRC, nil
+	return result, nil
 }
 
 //
@@ -354,8 +367,8 @@ func (bucket Bucket) DeleteObject(objectKey string) error {
 	if err != nil {
 		return err
 	}
-	defer resp.body.Close()
-	return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
+	defer resp.Body.Close()
+	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
 }
 
 //
@@ -394,10 +407,10 @@ func (bucket Bucket) DeleteObjects(objectKeys []string, options ...Option) (Dele
 	if err != nil {
 		return out, err
 	}
-	defer resp.body.Close()
+	defer resp.Body.Close()
 
 	if !dxml.Quiet {
-		if err = xmlUnmarshal(resp.body, &out); err == nil {
+		if err = xmlUnmarshal(resp.Body, &out); err == nil {
 			err = decodeDeleteObjectsResult(&out)
 		}
 	}
@@ -455,9 +468,9 @@ func (bucket Bucket) ListObjects(options ...Option) (ListObjectsResult, error) {
 	if err != nil {
 		return out, err
 	}
-	defer resp.body.Close()
+	defer resp.Body.Close()
 
-	err = xmlUnmarshal(resp.body, &out)
+	err = xmlUnmarshal(resp.Body, &out)
 	if err != nil {
 		return out, err
 	}
@@ -496,9 +509,9 @@ func (bucket Bucket) GetObjectDetailedMeta(objectKey string, options ...Option)
 	if err != nil {
 		return nil, err
 	}
-	defer resp.body.Close()
+	defer resp.Body.Close()
 
-	return resp.headers, nil
+	return resp.Headers, nil
 }
 
 //
@@ -517,9 +530,9 @@ func (bucket Bucket) GetObjectMeta(objectKey string) (http.Header, error) {
 	if err != nil {
 		return nil, err
 	}
-	defer resp.body.Close()
+	defer resp.Body.Close()
 
-	return resp.headers, nil
+	return resp.Headers, nil
 }
 
 //
@@ -545,8 +558,8 @@ func (bucket Bucket) SetObjectACL(objectKey string, objectACL ACLType) error {
 	if err != nil {
 		return err
 	}
-	defer resp.body.Close()
-	return checkRespCode(resp.statusCode, []int{http.StatusOK})
+	defer resp.Body.Close()
+	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 //
@@ -563,9 +576,9 @@ func (bucket Bucket) GetObjectACL(objectKey string) (GetObjectACLResult, error)
 	if err != nil {
 		return out, err
 	}
-	defer resp.body.Close()
+	defer resp.Body.Close()
 
-	err = xmlUnmarshal(resp.body, &out)
+	err = xmlUnmarshal(resp.Body, &out)
 	return out, err
 }
 

+ 42 - 42
oss/client.go

@@ -93,8 +93,8 @@ func (client Client) CreateBucket(bucketName string, options ...Option) error {
 		return err
 	}
 
-	defer resp.body.Close()
-	return checkRespCode(resp.statusCode, []int{http.StatusOK})
+	defer resp.Body.Close()
+	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 //
@@ -119,9 +119,9 @@ func (client Client) ListBuckets(options ...Option) (ListBucketsResult, error) {
 	if err != nil {
 		return out, err
 	}
-	defer resp.body.Close()
+	defer resp.Body.Close()
 
-	err = xmlUnmarshal(resp.body, &out)
+	err = xmlUnmarshal(resp.Body, &out)
 	return out, err
 }
 
@@ -158,8 +158,8 @@ func (client Client) DeleteBucket(bucketName string) error {
 		return err
 	}
 
-	defer resp.body.Close()
-	return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
+	defer resp.Body.Close()
+	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
 }
 
 //
@@ -178,10 +178,10 @@ func (client Client) GetBucketLocation(bucketName string) (string, error) {
 	if err != nil {
 		return "", err
 	}
-	defer resp.body.Close()
+	defer resp.Body.Close()
 
 	var LocationConstraint string
-	err = xmlUnmarshal(resp.body, &LocationConstraint)
+	err = xmlUnmarshal(resp.Body, &LocationConstraint)
 	return LocationConstraint, err
 }
 
@@ -200,8 +200,8 @@ func (client Client) SetBucketACL(bucketName string, bucketACL ACLType) error {
 	if err != nil {
 		return err
 	}
-	defer resp.body.Close()
-	return checkRespCode(resp.statusCode, []int{http.StatusOK})
+	defer resp.Body.Close()
+	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 //
@@ -218,9 +218,9 @@ func (client Client) GetBucketACL(bucketName string) (GetBucketACLResult, error)
 	if err != nil {
 		return out, err
 	}
-	defer resp.body.Close()
+	defer resp.Body.Close()
 
-	err = xmlUnmarshal(resp.body, &out)
+	err = xmlUnmarshal(resp.Body, &out)
 	return out, err
 }
 
@@ -255,8 +255,8 @@ func (client Client) SetBucketLifecycle(bucketName string, rules []LifecycleRule
 	if err != nil {
 		return err
 	}
-	defer resp.body.Close()
-	return checkRespCode(resp.statusCode, []int{http.StatusOK})
+	defer resp.Body.Close()
+	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 //
@@ -272,8 +272,8 @@ func (client Client) DeleteBucketLifecycle(bucketName string) error {
 	if err != nil {
 		return err
 	}
-	defer resp.body.Close()
-	return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
+	defer resp.Body.Close()
+	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
 }
 
 //
@@ -290,9 +290,9 @@ func (client Client) GetBucketLifecycle(bucketName string) (GetBucketLifecycleRe
 	if err != nil {
 		return out, err
 	}
-	defer resp.body.Close()
+	defer resp.Body.Close()
 
-	err = xmlUnmarshal(resp.body, &out)
+	err = xmlUnmarshal(resp.Body, &out)
 	return out, err
 }
 
@@ -338,8 +338,8 @@ func (client Client) SetBucketReferer(bucketName string, referers []string, allo
 	if err != nil {
 		return err
 	}
-	defer resp.body.Close()
-	return checkRespCode(resp.statusCode, []int{http.StatusOK})
+	defer resp.Body.Close()
+	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 //
@@ -356,9 +356,9 @@ func (client Client) GetBucketReferer(bucketName string) (GetBucketRefererResult
 	if err != nil {
 		return out, err
 	}
-	defer resp.body.Close()
+	defer resp.Body.Close()
 
-	err = xmlUnmarshal(resp.body, &out)
+	err = xmlUnmarshal(resp.Body, &out)
 	return out, err
 }
 
@@ -404,8 +404,8 @@ func (client Client) SetBucketLogging(bucketName, targetBucket, targetPrefix str
 	if err != nil {
 		return err
 	}
-	defer resp.body.Close()
-	return checkRespCode(resp.statusCode, []int{http.StatusOK})
+	defer resp.Body.Close()
+	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 //
@@ -420,8 +420,8 @@ func (client Client) DeleteBucketLogging(bucketName string) error {
 	if err != nil {
 		return err
 	}
-	defer resp.body.Close()
-	return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
+	defer resp.Body.Close()
+	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
 }
 
 //
@@ -438,9 +438,9 @@ func (client Client) GetBucketLogging(bucketName string) (GetBucketLoggingResult
 	if err != nil {
 		return out, err
 	}
-	defer resp.body.Close()
+	defer resp.Body.Close()
 
-	err = xmlUnmarshal(resp.body, &out)
+	err = xmlUnmarshal(resp.Body, &out)
 	return out, err
 }
 
@@ -476,8 +476,8 @@ func (client Client) SetBucketWebsite(bucketName, indexDocument, errorDocument s
 	if err != nil {
 		return err
 	}
-	defer resp.body.Close()
-	return checkRespCode(resp.statusCode, []int{http.StatusOK})
+	defer resp.Body.Close()
+	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 //
@@ -492,8 +492,8 @@ func (client Client) DeleteBucketWebsite(bucketName string) error {
 	if err != nil {
 		return err
 	}
-	defer resp.body.Close()
-	return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
+	defer resp.Body.Close()
+	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
 }
 
 //
@@ -510,9 +510,9 @@ func (client Client) GetBucketWebsite(bucketName string) (GetBucketWebsiteResult
 	if err != nil {
 		return out, err
 	}
-	defer resp.body.Close()
+	defer resp.Body.Close()
 
-	err = xmlUnmarshal(resp.body, &out)
+	err = xmlUnmarshal(resp.Body, &out)
 	return out, err
 }
 
@@ -553,8 +553,8 @@ func (client Client) SetBucketCORS(bucketName string, corsRules []CORSRule) erro
 	if err != nil {
 		return err
 	}
-	defer resp.body.Close()
-	return checkRespCode(resp.statusCode, []int{http.StatusOK})
+	defer resp.Body.Close()
+	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
 //
@@ -569,8 +569,8 @@ func (client Client) DeleteBucketCORS(bucketName string) error {
 	if err != nil {
 		return err
 	}
-	defer resp.body.Close()
-	return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
+	defer resp.Body.Close()
+	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
 }
 
 //
@@ -588,9 +588,9 @@ func (client Client) GetBucketCORS(bucketName string) (GetBucketCORSResult, erro
 	if err != nil {
 		return out, err
 	}
-	defer resp.body.Close()
+	defer resp.Body.Close()
 
-	err = xmlUnmarshal(resp.body, &out)
+	err = xmlUnmarshal(resp.Body, &out)
 	return out, err
 }
 
@@ -608,9 +608,9 @@ func (client Client) GetBucketInfo(bucketName string) (GetBucketInfoResult, erro
 	if err != nil {
 		return out, err
 	}
-	defer resp.body.Close()
+	defer resp.Body.Close()
 
-	err = xmlUnmarshal(resp.body, &out)
+	err = xmlUnmarshal(resp.Body, &out)
 	return out, err
 }
 

+ 6 - 0
oss/client_test.go

@@ -534,6 +534,9 @@ func (s *OssClientSuite) TestSetBucketLifecycle(c *C) {
 	err = client.SetBucketLifecycle(bucketNameTest, rules)
 	c.Assert(err, IsNil)
 
+	// eliminate effect of cache
+	time.Sleep(5 * time.Second)
+
 	res, err = client.GetBucketLifecycle(bucketNameTest)
 	c.Assert(err, IsNil)
 	c.Assert(len(res.Rules), Equals, 2)
@@ -860,6 +863,9 @@ func (s *OssClientSuite) TestSetBucketWebsite(c *C) {
 	err = client.SetBucketWebsite(bucketNameTest, indexWebsite, errorWebsite)
 	c.Assert(err, IsNil)
 
+	// eliminate effect of cache
+	time.Sleep(5 * time.Second)
+
 	res, err = client.GetBucketWebsite(bucketNameTest)
 	c.Assert(err, IsNil)
 	c.Assert(res.IndexDocument.Suffix, Equals, indexWebsite)

+ 11 - 20
oss/conn.go

@@ -24,15 +24,6 @@ type Conn struct {
 	url    *urlMaker
 }
 
-// Response Http response from oss
-type Response struct {
-	statusCode int
-	headers    http.Header
-	body       io.ReadCloser
-	clientCRC  uint64
-	serverCRC  uint64
-}
-
 // Do 处理请求,返回响应结果。
 func (conn Conn) Do(method, bucketName, objectName, urlParams, subResource string,
 	headers map[string]string, data io.Reader, initCRC uint64) (*Response, error) {
@@ -224,17 +215,17 @@ func (conn Conn) handleResponse(resp *http.Response, crc hash.Hash64) (*Response
 			err = srvErr
 		}
 		return &Response{
-			statusCode: resp.StatusCode,
-			headers:    resp.Header,
-			body:       ioutil.NopCloser(bytes.NewReader(respBody)), // restore the body
+			StatusCode: resp.StatusCode,
+			Headers:    resp.Header,
+			Body:       ioutil.NopCloser(bytes.NewReader(respBody)), // restore the body
 		}, err
 	} else if statusCode >= 300 && statusCode <= 307 {
 		// oss use 3xx, but response has no body
 		err := fmt.Errorf("oss: service returned %d,%s", resp.StatusCode, resp.Status)
 		return &Response{
-			statusCode: resp.StatusCode,
-			headers:    resp.Header,
-			body:       resp.Body,
+			StatusCode: resp.StatusCode,
+			Headers:    resp.Header,
+			Body:       resp.Body,
 		}, err
 	}
 
@@ -245,11 +236,11 @@ func (conn Conn) handleResponse(resp *http.Response, crc hash.Hash64) (*Response
 
 	// 2xx, successful
 	return &Response{
-		statusCode: resp.StatusCode,
-		headers:    resp.Header,
-		body:       resp.Body,
-		clientCRC:  cliCRC,
-		serverCRC:  srvCRC,
+		StatusCode: resp.StatusCode,
+		Headers:    resp.Header,
+		Body:       resp.Body,
+		ClientCRC:  cliCRC,
+		ServerCRC:  srvCRC,
 	}, nil
 }
 

+ 38 - 28
oss/crc_test.go

@@ -150,12 +150,12 @@ func (s *OssCrcSuite) TestEnableCRCAndMD5(c *C) {
 	body.Close()
 
 	// GetObjectWithCRC
-	body, calcCRC, srvCRC, err := bucket.GetObjectWithCRC(objectName)
+	getResult, err := bucket.DoGetObject(&GetObjectRequest{objectName}, nil)
 	c.Assert(err, IsNil)
-	str, err := readBody(body)
+	str, err := readBody(getResult.Response.Body)
 	c.Assert(err, IsNil)
 	c.Assert(str, Equals, objectValue)
-	c.Assert(calcCRC.Sum64(), Equals, srvCRC)
+	c.Assert(getResult.ClientCRC.Sum64(), Equals, getResult.ServerCRC)
 
 	// PutObjectFromFile
 	err = bucket.PutObjectFromFile(objectName, fileName)
@@ -174,19 +174,23 @@ func (s *OssCrcSuite) TestEnableCRCAndMD5(c *C) {
 
 	// AppendObject
 	var nextPos int64
-	nextPos, err = s.bucket.AppendObject(objectName, strings.NewReader(objectValue), nextPos)
+	nextPos, err = bucket.AppendObject(objectName, strings.NewReader(objectValue), nextPos)
 	c.Assert(err, IsNil)
-	nextPos, err = s.bucket.AppendObject(objectName, strings.NewReader(objectValue), nextPos)
+	nextPos, err = bucket.AppendObject(objectName, strings.NewReader(objectValue), nextPos)
 	c.Assert(err, IsNil)
 
-	err = s.bucket.DeleteObject(objectName)
+	err = bucket.DeleteObject(objectName)
 	c.Assert(err, IsNil)
 
-	nextPos = 0
-	var initCRC uint64
-	nextPos, initCRC, err = s.bucket.AppendObjectWithCRC(objectName, strings.NewReader(objectValue), nextPos, initCRC)
+	request := &AppendObjectRequest{
+		ObjectKey: objectName,
+		Reader:    strings.NewReader(objectValue),
+		Position:  0,
+	}
+	appendResult, err := bucket.DoAppendObject(request, []Option{InitCRC(0)})
 	c.Assert(err, IsNil)
-	nextPos, initCRC, err = s.bucket.AppendObjectWithCRC(objectName, strings.NewReader(objectValue), nextPos, initCRC)
+	request.Position = appendResult.NextPosition
+	appendResult, err = bucket.DoAppendObject(request, []Option{InitCRC(appendResult.CRC)})
 	c.Assert(err, IsNil)
 
 	err = s.bucket.DeleteObject(objectName)
@@ -243,9 +247,9 @@ func (s *OssCrcSuite) TestDisableCRCAndMD5(c *C) {
 	body.Close()
 
 	// GetObjectWithCRC
-	body, _, _, err = bucket.GetObjectWithCRC(objectName)
+	getResult, err := bucket.DoGetObject(&GetObjectRequest{objectName}, nil)
 	c.Assert(err, IsNil)
-	str, err := readBody(body)
+	str, err := readBody(getResult.Response.Body)
 	c.Assert(err, IsNil)
 	c.Assert(str, Equals, objectValue)
 
@@ -266,19 +270,23 @@ func (s *OssCrcSuite) TestDisableCRCAndMD5(c *C) {
 
 	// AppendObject
 	var nextPos int64
-	nextPos, err = s.bucket.AppendObject(objectName, strings.NewReader(objectValue), nextPos)
+	nextPos, err = bucket.AppendObject(objectName, strings.NewReader(objectValue), nextPos)
 	c.Assert(err, IsNil)
-	nextPos, err = s.bucket.AppendObject(objectName, strings.NewReader(objectValue), nextPos)
+	nextPos, err = bucket.AppendObject(objectName, strings.NewReader(objectValue), nextPos)
 	c.Assert(err, IsNil)
 
-	err = s.bucket.DeleteObject(objectName)
+	err = bucket.DeleteObject(objectName)
 	c.Assert(err, IsNil)
 
-	nextPos = 0
-	var initCRC uint64
-	nextPos, initCRC, err = s.bucket.AppendObjectWithCRC(objectName, strings.NewReader(objectValue), nextPos, initCRC)
+	request := &AppendObjectRequest{
+		ObjectKey: objectName,
+		Reader:    strings.NewReader(objectValue),
+		Position:  0,
+	}
+	appendResult, err := bucket.DoAppendObject(request, []Option{InitCRC(0)})
 	c.Assert(err, IsNil)
-	nextPos, initCRC, err = s.bucket.AppendObjectWithCRC(objectName, strings.NewReader(objectValue), nextPos, initCRC)
+	request.Position = appendResult.NextPosition
+	appendResult, err = bucket.DoAppendObject(request, []Option{InitCRC(appendResult.CRC)})
 	c.Assert(err, IsNil)
 
 	err = s.bucket.DeleteObject(objectName)
@@ -311,7 +319,7 @@ func (s *OssCrcSuite) TestDisableCRCAndMD5(c *C) {
 	c.Assert(err, IsNil)
 }
 
-// TestDisableCRCAndMD5 关闭MD5和CRC校验
+// TestSpecifyContentMD5 指定MD5
 func (s *OssCrcSuite) TestSpecifyContentMD5(c *C) {
 	objectName := objectNamePrefix + "tdcam"
 	fileName := "../sample/BingWallpaper-2015-11-07.jpg"
@@ -345,11 +353,15 @@ func (s *OssCrcSuite) TestSpecifyContentMD5(c *C) {
 	err = s.bucket.DeleteObject(objectName)
 	c.Assert(err, IsNil)
 
-	nextPos = 0
-	var initCRC uint64
-	nextPos, initCRC, err = s.bucket.AppendObjectWithCRC(objectName, strings.NewReader(objectValue), nextPos, initCRC)
+	request := &AppendObjectRequest{
+		ObjectKey: objectName,
+		Reader:    strings.NewReader(objectValue),
+		Position:  0,
+	}
+	appendResult, err := s.bucket.DoAppendObject(request, []Option{InitCRC(0)})
 	c.Assert(err, IsNil)
-	nextPos, initCRC, err = s.bucket.AppendObjectWithCRC(objectName, strings.NewReader(objectValue), nextPos, initCRC)
+	request.Position = appendResult.NextPosition
+	appendResult, err = s.bucket.DoAppendObject(request, []Option{InitCRC(appendResult.CRC)})
 	c.Assert(err, IsNil)
 
 	err = s.bucket.DeleteObject(objectName)
@@ -376,13 +388,11 @@ func (s *OssCrcSuite) TestSpecifyContentMD5(c *C) {
 func (s *OssCrcSuite) TestAppendObjectNegative(c *C) {
 	objectName := objectNamePrefix + "taoncrc"
 	objectValue := "空山不见人,但闻人语响。返影入深林,复照青苔上。"
-	var nextPos int64
-	var initCRC uint64
 
-	nextPos, initCRC, err := s.bucket.AppendObjectWithCRC(objectName, strings.NewReader(objectValue), nextPos, initCRC)
+	nextPos, err := s.bucket.AppendObject(objectName, strings.NewReader(objectValue), 0, InitCRC(0))
 	c.Assert(err, IsNil)
 
-	nextPos, initCRC, err = s.bucket.AppendObjectWithCRC(objectName, strings.NewReader(objectValue), nextPos, initCRC-1)
+	nextPos, err = s.bucket.AppendObject(objectName, strings.NewReader(objectValue), nextPos, InitCRC(0))
 	c.Assert(err, NotNil)
 	c.Assert(strings.HasPrefix(err.Error(), "oss: the crc"), Equals, true)
 }

+ 2 - 2
oss/error.go

@@ -75,8 +75,8 @@ func (e CRCCheckError) Error() string {
 }
 
 func checkCRC(resp *Response, operation string) error {
-	if resp.clientCRC == resp.serverCRC {
+	if resp.ClientCRC == resp.ServerCRC {
 		return nil
 	}
-	return CRCCheckError{resp.clientCRC, resp.serverCRC, operation, resp.headers.Get(HTTPHeaderOssRequestID)}
+	return CRCCheckError{resp.ClientCRC, resp.ServerCRC, operation, resp.Headers.Get(HTTPHeaderOssRequestID)}
 }

+ 60 - 0
oss/model.go

@@ -0,0 +1,60 @@
+package oss
+
+import (
+	"hash"
+	"io"
+	"net/http"
+)
+
+// Response Http response from oss
+type Response struct {
+	StatusCode int
+	Headers    http.Header
+	Body       io.ReadCloser
+	ClientCRC  uint64
+	ServerCRC  uint64
+}
+
+// PutObjectRequest The request of DoPutObject
+type PutObjectRequest struct {
+	ObjectKey string
+	Reader    io.Reader
+}
+
+// GetObjectRequest The request of DoGetObject
+type GetObjectRequest struct {
+	ObjectKey string
+}
+
+// GetObjectResult The result of DoGetObject
+type GetObjectResult struct {
+	Response  *Response
+	ClientCRC hash.Hash64
+	ServerCRC uint64
+}
+
+// AppendObjectRequest  The requtest of DoAppendObject
+type AppendObjectRequest struct {
+	ObjectKey string
+	Reader    io.Reader
+	Position  int64
+}
+
+// AppendObjectResult The result of DoAppendObject
+type AppendObjectResult struct {
+	NextPosition int64
+	CRC          uint64
+}
+
+// UploadPartRequest The request of DoUploadPart
+type UploadPartRequest struct {
+	InitResult *InitiateMultipartUploadResult
+	Reader     io.Reader
+	PartSize   int64
+	PartNumber int
+}
+
+// UploadPartResult The result of DoUploadPart
+type UploadPartResult struct {
+	Part UploadPart
+}

+ 55 - 38
oss/multipart.go

@@ -28,9 +28,9 @@ func (bucket Bucket) InitiateMultipartUpload(objectKey string, options ...Option
 	if err != nil {
 		return imur, err
 	}
-	defer resp.body.Close()
+	defer resp.Body.Close()
 
-	err = xmlUnmarshal(resp.body, &imur)
+	err = xmlUnmarshal(resp.Body, &imur)
 	return imur, err
 }
 
@@ -53,26 +53,17 @@ func (bucket Bucket) InitiateMultipartUpload(objectKey string, options ...Option
 // error 操作成功error为nil,非nil为错误信息。
 //
 func (bucket Bucket) UploadPart(imur InitiateMultipartUploadResult, reader io.Reader,
-	size int64, partNumber int) (UploadPart, error) {
-	var part = UploadPart{}
-	params := "partNumber=" + strconv.Itoa(partNumber) + "&uploadId=" + imur.UploadID
-	opts := []Option{ContentLength(size)}
-	resp, err := bucket.do("PUT", imur.Key, params, params, opts, &io.LimitedReader{R: reader, N: size})
-	if err != nil {
-		return part, err
+	partSize int64, partNumber int) (UploadPart, error) {
+	request := &UploadPartRequest{
+		InitResult: &imur,
+		Reader:     reader,
+		PartSize:   partSize,
+		PartNumber: partNumber,
 	}
-	defer resp.body.Close()
 
-	if bucket.getConfig().IsEnableCRC {
-		err = checkCRC(resp, "UploadPart")
-		if err != nil {
-			return part, err
-		}
-	}
+	result, err := bucket.DoUploadPart(request)
 
-	part.ETag = resp.headers.Get(HTTPHeaderEtag)
-	part.PartNumber = partNumber
-	return part, nil
+	return result.Part, err
 }
 
 //
@@ -98,23 +89,49 @@ func (bucket Bucket) UploadPartFromFile(imur InitiateMultipartUploadResult, file
 	defer fd.Close()
 	fd.Seek(startPosition, os.SEEK_SET)
 
-	params := "partNumber=" + strconv.Itoa(partNumber) + "&uploadId=" + imur.UploadID
-	resp, err := bucket.do("PUT", imur.Key, params, params, nil, &io.LimitedReader{R: fd, N: partSize})
+	request := &UploadPartRequest{
+		InitResult: &imur,
+		Reader:     fd,
+		PartSize:   partSize,
+		PartNumber: partNumber,
+	}
+
+	result, err := bucket.DoUploadPart(request)
+
+	return result.Part, err
+}
+
+//
+// DoUploadPart 上传分片。
+//
+// request 上传分片请求。
+//
+// UploadPartResult 上传分片请求返回值。
+// error  操作无错误为nil,非nil为错误信息。
+//
+func (bucket Bucket) DoUploadPart(request *UploadPartRequest) (*UploadPartResult, error) {
+	params := "partNumber=" + strconv.Itoa(request.PartNumber) + "&uploadId=" + request.InitResult.UploadID
+	opts := []Option{ContentLength(request.PartSize)}
+	resp, err := bucket.do("PUT", request.InitResult.Key, params, params, opts,
+		&io.LimitedReader{R: request.Reader, N: request.PartSize})
 	if err != nil {
-		return part, err
+		return &UploadPartResult{}, err
+	}
+	defer resp.Body.Close()
+
+	part := UploadPart{
+		ETag:       resp.Headers.Get(HTTPHeaderEtag),
+		PartNumber: request.PartNumber,
 	}
-	defer resp.body.Close()
 
 	if bucket.getConfig().IsEnableCRC {
-		err = checkCRC(resp, "UploadPartFromFile")
+		err = checkCRC(resp, "DoUploadPart")
 		if err != nil {
-			return part, err
+			return &UploadPartResult{part}, err
 		}
 	}
 
-	part.ETag = resp.headers.Get(HTTPHeaderEtag)
-	part.PartNumber = partNumber
-	return part, nil
+	return &UploadPartResult{part}, nil
 }
 
 //
@@ -146,9 +163,9 @@ func (bucket Bucket) UploadPartCopy(imur InitiateMultipartUploadResult, srcBucke
 	if err != nil {
 		return part, err
 	}
-	defer resp.body.Close()
+	defer resp.Body.Close()
 
-	err = xmlUnmarshal(resp.body, &out)
+	err = xmlUnmarshal(resp.Body, &out)
 	if err != nil {
 		return part, err
 	}
@@ -186,9 +203,9 @@ func (bucket Bucket) CompleteMultipartUpload(imur InitiateMultipartUploadResult,
 	if err != nil {
 		return out, err
 	}
-	defer resp.body.Close()
+	defer resp.Body.Close()
 
-	err = xmlUnmarshal(resp.body, &out)
+	err = xmlUnmarshal(resp.Body, &out)
 	return out, err
 }
 
@@ -205,8 +222,8 @@ func (bucket Bucket) AbortMultipartUpload(imur InitiateMultipartUploadResult) er
 	if err != nil {
 		return err
 	}
-	defer resp.body.Close()
-	return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
+	defer resp.Body.Close()
+	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
 }
 
 //
@@ -224,9 +241,9 @@ func (bucket Bucket) ListUploadedParts(imur InitiateMultipartUploadResult) (List
 	if err != nil {
 		return out, err
 	}
-	defer resp.body.Close()
+	defer resp.Body.Close()
 
-	err = xmlUnmarshal(resp.body, &out)
+	err = xmlUnmarshal(resp.Body, &out)
 	return out, err
 }
 
@@ -252,9 +269,9 @@ func (bucket Bucket) ListMultipartUploads(options ...Option) (ListMultipartUploa
 	if err != nil {
 		return out, err
 	}
-	defer resp.body.Close()
+	defer resp.Body.Close()
 
-	err = xmlUnmarshal(resp.body, &out)
+	err = xmlUnmarshal(resp.Body, &out)
 	if err != nil {
 		return out, err
 	}

+ 6 - 0
oss/option.go

@@ -23,6 +23,7 @@ const (
 	deleteObjectsQuiet = "delete-objects-quiet"
 	routineNum         = "x-routine-num"
 	checkpointConfig   = "x-cp-config"
+	initCRC64          = "init-crc64"
 )
 
 type (
@@ -223,6 +224,11 @@ func Routines(n int) Option {
 	return addArg(routineNum, strconv.Itoa(n))
 }
 
+// InitCRC AppendObject CRC的校验的初始值
+func InitCRC(initCRC uint64) Option {
+	return addArg(initCRC64, strconv.FormatUint(initCRC, 10))
+}
+
 func setHeader(key, value string) Option {
 	return func(params map[string]optionValue) error {
 		if value == "" {