func (getObjectRequest *GetObjectRequest) MergeToSignOption(option *bce.SignOption) {
	if getObjectRequest.Range != "" {
		option.AddHeader("Range", "bytes="+getObjectRequest.Range)
	}
}
MergeToSignOption merges bos.GetObjectRequest fields to bce.SignOption.
package bos
import (
	"fmt"
	"io"
	"net/http"
	"sort"
	"strconv"
	"strings"
	"time"
	"github.com/baidu/baiducloud-sdk-go/bce"
	"github.com/baidu/baiducloud-sdk-go/util"
)
const MIN_PART_NUMBER int = 1
const MAX_PART_NUMBER int = 10000
const STORAGE_CLASS_STANDARD = "STANDARD"
const STORAGE_CLASS_STANDARD_IA = "STANDARD_IA"
const STORAGE_CLASS_COLD = "COLD"
const UserDefinedMetadataPrefix = "x-bce-meta-"
var CannedAccessControlList = map[string]string{
	"Private":         "private",
	"PublicRead":      "public-read",
	"PublicReadWrite": "public-read-write",
}
type Location struct {
	LocationConstraint string
}
type BucketOwner struct {
	Id          string `json:"id"`
	DisplayName string `json:"displayName,omitempty"`
}
type Bucket struct {
	Name, Location string
	CreationDate   time.Time
}
type BucketSummary struct {
	Owner   BucketOwner
	Buckets []Bucket
}
type BucketAcl struct {
	Owner             BucketOwner `json:"owner"`
	AccessControlList []Grant     `json:"accessControlList"`
}
type Grant struct {
	Grantee    []BucketGrantee `json:"grantee"`
	Permission []string        `json:"permission"`
}
type BucketGrantee struct {
	Id string `json:"id"`
}
type ObjectMetadata struct {
	CacheControl       string
	ContentDisposition string
	ContentLength      int64
	ContentMD5         string
	ContentType        string
	Expires            string
	ContentSha256      string
	ContentRange string
	ETag         string
	UserMetadata map[string]string
	StorageClass string
}
func NewObjectMetadataFromHeader(h http.Header) *ObjectMetadata {
	objectMetadata := &ObjectMetadata{}
	for key := range h {
		if len(h[key]) > 0 {
			lowerKey := strings.ToLower(key)
			value := h[key][0]
			if lowerKey == "cache-control" {
				objectMetadata.CacheControl = value
			} else if lowerKey == "content-disposition" {
				objectMetadata.ContentDisposition = value
			} else if lowerKey == "content-length" {
				length, err := strconv.ParseInt(value, 10, 64)
				if err == nil {
					objectMetadata.ContentLength = length
				}
			} else if lowerKey == "content-range" {
				objectMetadata.ContentRange = value
			} else if lowerKey == "content-type" {
				objectMetadata.ContentType = value
			} else if lowerKey == "expires" {
				objectMetadata.Expires = value
			} else if lowerKey == "etag" {
				objectMetadata.ETag = strings.Replace(value, "\"", "", -1)
			} else if IsUserDefinedMetadata(lowerKey) {
				if objectMetadata.UserMetadata == nil {
					objectMetadata.UserMetadata = make(map[string]string, 0)
				}
				objectMetadata.UserMetadata[key] = h[key][0]
			} else if lowerKey == "x-bce-storage-class" {
				objectMetadata.StorageClass = value
			}
		}
	}
	return objectMetadata
}
func (metadata *ObjectMetadata) AddUserMetadata(key, value string) {
	if metadata.UserMetadata == nil {
		metadata.UserMetadata = make(map[string]string)
	}
	metadata.UserMetadata[key] = value
}
func (metadata *ObjectMetadata) mergeToSignOption(option *bce.SignOption) {
	if metadata.CacheControl != "" {
		option.AddHeader("Cache-Control", metadata.CacheControl)
	}
	if metadata.ContentDisposition != "" {
		option.AddHeader("Content-Disposition", metadata.ContentDisposition)
	}
	if metadata.ContentLength != 0 {
		option.AddHeader("Content-Length", strconv.FormatInt(metadata.ContentLength, 10))
	}
	if metadata.ContentMD5 != "" {
		option.AddHeader("Content-MD5", metadata.ContentMD5)
	}
	if metadata.ContentType != "" {
		option.AddHeader("Content-Type", metadata.ContentType)
	}
	if metadata.Expires != "" {
		option.AddHeader("Expires", metadata.Expires)
	}
	if metadata.ContentSha256 != "" {
		option.AddHeader("x-bce-content-sha256", metadata.ContentSha256)
	}
	if metadata.StorageClass == STORAGE_CLASS_STANDARD ||
		metadata.StorageClass == STORAGE_CLASS_STANDARD_IA ||
		metadata.StorageClass == STORAGE_CLASS_COLD {
		option.AddHeader("x-bce-storage-class", metadata.StorageClass)
	}
	for key, value := range metadata.UserMetadata {
		option.AddHeader(ToUserDefinedMetadata(key), value)
	}
}
type PutObjectResponse http.Header
func NewPutObjectResponse(h http.Header) PutObjectResponse {
	return PutObjectResponse(h)
}
func (res PutObjectResponse) Get(key string) string {
	return http.Header(res).Get(key)
}
func (res PutObjectResponse) GetETag() string {
	return strings.Replace(res.Get("Etag"), "\"", "", -1)
}
type AppendObjectResponse http.Header
func NewAppendObjectResponse(h http.Header) AppendObjectResponse {
	return AppendObjectResponse(h)
}
func (res AppendObjectResponse) Get(key string) string {
	return http.Header(res).Get(key)
}
func (res AppendObjectResponse) GetETag() string {
	return strings.Replace(res.Get("Etag"), "\"", "", -1)
}
func (res AppendObjectResponse) GetMD5() string {
	return res.Get("Content-MD5")
}
func (res AppendObjectResponse) GetNextAppendOffset() string {
	return res.Get("x-bce-next-append-offset")
}
type ObjectSummary struct {
	Key          string
	LastModified string
	ETag         string
	Size         int64
	Owner        BucketOwner
}
type ListObjectsRequest struct {
	BucketName, Delimiter, Marker, Prefix string
	MaxKeys                               int
}
type ListObjectsResponse struct {
	Name           string
	Prefix         string
	Delimiter      string
	Marker         string
	NextMarker     string
	MaxKeys        uint
	IsTruncated    bool
	Contents       []ObjectSummary
	CommonPrefixes []map[string]string
}
func (listObjectsResponse *ListObjectsResponse) GetCommonPrefixes() []string {
	prefixes := make([]string, 0, len(listObjectsResponse.CommonPrefixes))
	for _, commonPrefix := range listObjectsResponse.CommonPrefixes {
		prefixes = append(prefixes, commonPrefix["prefix"])
	}
	return prefixes
}
type CopyObjectResponse struct {
	ETag         string
	LastModified time.Time
}
type CopyObjectRequest struct {
	SrcBucketName         string          `json:"-"`
	SrcKey                string          `json:"-"`
	DestBucketName        string          `json:"-"`
	DestKey               string          `json:"-"`
	ObjectMetadata        *ObjectMetadata `json:"-"`
	SourceMatch           string          `json:"x-bce-copy-source-if-match,omitempty"`
	SourceNoneMatch       string          `json:"x-bce-copy-source-if-none-match,omitempty"`
	SourceModifiedSince   string          `json:"x-bce-copy-source-if-modified-since,omitempty"`
	SourceUnmodifiedSince string          `json:"x-bce-copy-source-if-unmodified-since,omitempty"`
}
func (copyObjectRequest CopyObjectRequest) mergeToSignOption(option *bce.SignOption) {
	m, err := util.ToMap(copyObjectRequest)
	if err != nil {
		return
	}
	headerMap := make(map[string]string)
	for key, value := range m {
		if str, ok := value.(string); ok {
			headerMap[key] = str
		}
	}
	option.AddHeaders(headerMap)
	if copyObjectRequest.ObjectMetadata != nil {
		option.AddHeader("x-bce-metadata-directive", "replace")
		copyObjectRequest.ObjectMetadata.mergeToSignOption(option)
	} else {
		option.AddHeader("x-bce-metadata-directive", "copy")
	}
}
type Object struct {
	ObjectMetadata *ObjectMetadata
	ObjectContent  io.ReadCloser
}
type GetObjectRequest struct {
	BucketName string
	ObjectKey  string
	Range      string
}