/* * Copyright 2017 Baidu, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the specific language governing permissions * and limitations under the License. */ // object.go - the object APIs definition supported by the BOS service package api import ( "encoding/json" "fmt" "net" "strconv" "strings" "github.com/baidubce/bce-sdk-go/auth" "github.com/baidubce/bce-sdk-go/bce" "github.com/baidubce/bce-sdk-go/http" "github.com/baidubce/bce-sdk-go/util" ) // PutObject - put the object from the string or the stream // // PARAMS: // - cli: the client agent which can perform sending request // - bucket: the bucket name of the object // - object: the name of the object // - body: the input content of the object // - args: the optional arguments of this api // RETURNS: // - string: the etag of the object // - error: nil if ok otherwise the specific error func PutObject(cli bce.Client, bucket, object string, body *bce.Body, args *PutObjectArgs) (string, error) { req := &bce.BceRequest{} req.SetUri(getObjectUri(bucket, object)) req.SetMethod(http.PUT) if body == nil { return "", bce.NewBceClientError("PutObject body should not be emtpy") } if body.Size() >= THRESHOLD_100_CONTINUE { req.SetHeader("Expect", "100-continue") } req.SetBody(body) // Optional arguments settings if args != nil { setOptionalNullHeaders(req, map[string]string{ http.CACHE_CONTROL: args.CacheControl, http.CONTENT_DISPOSITION: args.ContentDisposition, http.CONTENT_TYPE: args.ContentType, http.EXPIRES: args.Expires, http.BCE_CONTENT_SHA256: args.ContentSha256, http.BCE_CONTENT_CRC32: args.ContentCrc32, }) if args.ContentLength > 0 { // User specified Content-Length can be smaller than the body size, so the body should // be reset. The `net/http.Client' does not support the Content-Length bigger than the // body size. if args.ContentLength > body.Size() { return "", bce.NewBceClientError(fmt.Sprintf("ContentLength %d is bigger than body size %d", args.ContentLength, body.Size())) } body, err := bce.NewBodyFromSizedReader(body.Stream(), args.ContentLength) if err != nil { return "", bce.NewBceClientError(err.Error()) } req.SetHeader(http.CONTENT_LENGTH, fmt.Sprintf("%d", args.ContentLength)) req.SetBody(body) // re-assign body } //set traffic-limit if args.TrafficLimit > 0 { if args.TrafficLimit > TRAFFIC_LIMIT_MAX || args.TrafficLimit < TRAFFIC_LIMIT_MIN { return "", bce.NewBceClientError(fmt.Sprintf("TrafficLimit must between %d ~ %d, current value:%d", TRAFFIC_LIMIT_MIN, TRAFFIC_LIMIT_MAX, args.TrafficLimit)) } req.SetHeader(http.BCE_TRAFFIC_LIMIT, fmt.Sprintf("%d", args.TrafficLimit)) } // Reset the contentMD5 if set by user if len(args.ContentMD5) != 0 { req.SetHeader(http.CONTENT_MD5, args.ContentMD5) } if validStorageClass(args.StorageClass) { req.SetHeader(http.BCE_STORAGE_CLASS, args.StorageClass) } else { if len(args.StorageClass) != 0 { return "", bce.NewBceClientError("invalid storage class value: " + args.StorageClass) } } if err := setUserMetadata(req, args.UserMeta); err != nil { return "", err } if len(args.Process) != 0 { req.SetHeader(http.BCE_PROCESS, args.Process) } } // add content-type if not assigned by user if req.Header(http.CONTENT_TYPE) == "" { req.SetHeader(http.CONTENT_TYPE, getDefaultContentType(object)) } resp := &bce.BceResponse{} if err := SendRequest(cli, req, resp); err != nil { return "", err } if resp.IsFail() { return "", resp.ServiceError() } defer func() { resp.Body().Close() }() return strings.Trim(resp.Header(http.ETAG), "\""), nil } // CopyObject - copy one object to a new object with new bucket and/or name. It can alse set the // metadata of the object with the same source and target. // // PARAMS: // - cli: the client object which can perform sending request // - bucket: the bucket name of the target object // - object: the name of the target object // - source: the source object uri // - *CopyObjectArgs: the optional input args for copying object // RETURNS: // - *CopyObjectResult: the result object which contains etag and lastmodified // - error: nil if ok otherwise the specific error func CopyObject(cli bce.Client, bucket, object, source string, args *CopyObjectArgs) (*CopyObjectResult, error) { req := &bce.BceRequest{} req.SetUri(getObjectUri(bucket, object)) req.SetMethod(http.PUT) if len(source) == 0 { return nil, bce.NewBceClientError("copy source should not be null") } req.SetHeader(http.BCE_COPY_SOURCE, util.UriEncode(source, false)) // Optional arguments settings if args != nil { setOptionalNullHeaders(req, map[string]string{ http.CACHE_CONTROL: args.CacheControl, http.CONTENT_DISPOSITION: args.ContentDisposition, http.CONTENT_ENCODING: args.ContentEncoding, http.CONTENT_RANGE: args.ContentRange, http.CONTENT_TYPE: args.ContentType, http.EXPIRES: args.Expires, http.LAST_MODIFIED: args.LastModified, http.ETAG: args.ETag, http.CONTENT_MD5: args.ContentMD5, http.BCE_CONTENT_SHA256: args.ContentSha256, http.BCE_OBJECT_TYPE: args.ObjectType, http.BCE_NEXT_APPEND_OFFSET: args.NextAppendOffset, http.BCE_COPY_SOURCE_IF_MATCH: args.IfMatch, http.BCE_COPY_SOURCE_IF_NONE_MATCH: args.IfNoneMatch, http.BCE_COPY_SOURCE_IF_MODIFIED_SINCE: args.IfModifiedSince, http.BCE_COPY_SOURCE_IF_UNMODIFIED_SINCE: args.IfUnmodifiedSince, }) if args.ContentLength != 0 { req.SetHeader(http.CONTENT_LENGTH, fmt.Sprintf("%d", args.ContentLength)) } if validMetadataDirective(args.MetadataDirective) { req.SetHeader(http.BCE_COPY_METADATA_DIRECTIVE, args.MetadataDirective) } else { if len(args.MetadataDirective) != 0 { return nil, bce.NewBceClientError( "invalid metadata directive value: " + args.MetadataDirective) } } if validStorageClass(args.StorageClass) { req.SetHeader(http.BCE_STORAGE_CLASS, args.StorageClass) } else { if len(args.StorageClass) != 0 { return nil, bce.NewBceClientError("invalid storage class value: " + args.StorageClass) } } //set traffic-limit if args.TrafficLimit > 0 { if args.TrafficLimit > TRAFFIC_LIMIT_MAX || args.TrafficLimit < TRAFFIC_LIMIT_MIN { return nil, bce.NewBceClientError(fmt.Sprintf("TrafficLimit must between %d ~ %d, current value:%d", TRAFFIC_LIMIT_MIN, TRAFFIC_LIMIT_MAX, args.TrafficLimit)) } req.SetHeader(http.BCE_TRAFFIC_LIMIT, fmt.Sprintf("%d", args.TrafficLimit)) } if validCannedAcl(args.CannedAcl) { req.SetHeader(http.BCE_ACL, args.CannedAcl) } if err := setUserMetadata(req, args.UserMeta); err != nil { return nil, err } } // Send request and get the result resp := &bce.BceResponse{} if err := SendRequest(cli, req, resp); err != nil { return nil, err } if resp.IsFail() { return nil, resp.ServiceError() } jsonBody := &CopyObjectResult{} if err := resp.ParseJsonBody(jsonBody); err != nil { return nil, err } return jsonBody, nil } // GetObject - get the object content with range and response-headers-specified support // // PARAMS: // - cli: the client agent which can perform sending request // - bucket: the bucket name of the object // - object: the name of the object // - args: the optional args in querysring // - ranges: the optional range start and end to get the given object // RETURNS: // - *GetObjectResult: the output content result of the object // - error: nil if ok otherwise the specific error func GetObject(cli bce.Client, bucket, object string, args map[string]string, // nolint:gocyclo ranges ...int64) (*GetObjectResult, error) { if object == "" { err := fmt.Errorf("Get Object don't accept \"\" as a parameter") return nil, err } req := &bce.BceRequest{} req.SetUri(getObjectUri(bucket, object)) req.SetMethod(http.GET) // Optional arguments settings if args != nil { for k, v := range args { if _, ok := GET_OBJECT_ALLOWED_RESPONSE_HEADERS[k]; ok { req.SetParam("response"+k, v) } if strings.HasPrefix(k, http.BCE_PREFIX) { req.SetParam(k, v) } } } if len(ranges) != 0 { rangeStr := "bytes=" if len(ranges) == 1 { rangeStr += fmt.Sprintf("%d", ranges[0]) + "-" } else { rangeStr += fmt.Sprintf("%d", ranges[0]) + "-" + fmt.Sprintf("%d", ranges[1]) } req.SetHeader("Range", rangeStr) } // Send request and get the result resp := &bce.BceResponse{} if err := SendRequest(cli, req, resp); err != nil { return nil, err } if resp.IsFail() { return nil, resp.ServiceError() } headers := resp.Headers() result := &GetObjectResult{} if val, ok := headers[http.CACHE_CONTROL]; ok { result.CacheControl = val } if val, ok := headers[http.CONTENT_DISPOSITION]; ok { result.ContentDisposition = val } if val, ok := headers[http.CONTENT_LENGTH]; ok { if length, err := strconv.ParseInt(val, 10, 64); err == nil { result.ContentLength = length } } if val, ok := headers[http.CONTENT_RANGE]; ok { result.ContentRange = val } if val, ok := headers[http.CONTENT_TYPE]; ok { result.ContentType = val } if val, ok := headers[http.CONTENT_MD5]; ok { result.ContentMD5 = val } if val, ok := headers[http.EXPIRES]; ok { result.Expires = val } if val, ok := headers[http.LAST_MODIFIED]; ok { result.LastModified = val } if val, ok := headers[http.ETAG]; ok { result.ETag = strings.Trim(val, "\"") } if val, ok := headers[http.CONTENT_LANGUAGE]; ok { result.ContentLanguage = val } if val, ok := headers[http.CONTENT_ENCODING]; ok { result.ContentEncoding = val } if val, ok := headers[toHttpHeaderKey(http.BCE_CONTENT_SHA256)]; ok { result.ContentSha256 = val } if val, ok := headers[toHttpHeaderKey(http.BCE_CONTENT_CRC32)]; ok { result.ContentCrc32 = val } if val, ok := headers[toHttpHeaderKey(http.BCE_STORAGE_CLASS)]; ok { result.StorageClass = val } bcePrefix := toHttpHeaderKey(http.BCE_USER_METADATA_PREFIX) for k, v := range headers { if strings.Index(k, bcePrefix) == 0 { if result.UserMeta == nil { result.UserMeta = make(map[string]string) } result.UserMeta[k[len(bcePrefix):]] = v } } if val, ok := headers[toHttpHeaderKey(http.BCE_OBJECT_TYPE)]; ok { result.ObjectType = val } if val, ok := headers[toHttpHeaderKey(http.BCE_NEXT_APPEND_OFFSET)]; ok { result.NextAppendOffset = val } result.Body = resp.Body() return result, nil } // GetObjectMeta - get the meta data of the given object // // PARAMS: // - cli: the client agent which can perform sending request // - bucket: the bucket name of the object // - object: the name of the object // RETURNS: // - *GetObjectMetaResult: the result of this api // - error: nil if ok otherwise the specific error func GetObjectMeta(cli bce.Client, bucket, object string) (*GetObjectMetaResult, error) { req := &bce.BceRequest{} req.SetUri(getObjectUri(bucket, object)) req.SetMethod(http.HEAD) // Send request and get the result resp := &bce.BceResponse{} if err := SendRequest(cli, req, resp); err != nil { return nil, err } if resp.IsFail() { return nil, resp.ServiceError() } headers := resp.Headers() result := &GetObjectMetaResult{} if val, ok := headers[http.CACHE_CONTROL]; ok { result.CacheControl = val } if val, ok := headers[http.CONTENT_DISPOSITION]; ok { result.ContentDisposition = val } if val, ok := headers[http.CONTENT_LENGTH]; ok { if length, err := strconv.ParseInt(val, 10, 64); err == nil { result.ContentLength = length } } if val, ok := headers[http.CONTENT_RANGE]; ok { result.ContentRange = val } if val, ok := headers[http.CONTENT_TYPE]; ok { result.ContentType = val } if val, ok := headers[http.CONTENT_MD5]; ok { result.ContentMD5 = val } if val, ok := headers[http.EXPIRES]; ok { result.Expires = val } if val, ok := headers[http.LAST_MODIFIED]; ok { result.LastModified = val } if val, ok := headers[http.ETAG]; ok { result.ETag = strings.Trim(val, "\"") } if val, ok := headers[http.CONTENT_ENCODING]; ok { result.ContentEncoding = val } if val, ok := headers[toHttpHeaderKey(http.BCE_CONTENT_SHA256)]; ok { result.ContentSha256 = val } if val, ok := headers[toHttpHeaderKey(http.BCE_CONTENT_CRC32)]; ok { result.ContentCrc32 = val } if val, ok := headers[toHttpHeaderKey(http.BCE_STORAGE_CLASS)]; ok { result.StorageClass = val } if val, ok := headers[toHttpHeaderKey(http.BCE_RESTORE)]; ok { result.BceRestore = val } if val, ok := headers[http.BCE_OBJECT_TYPE]; ok { result.BceObjectType = val } bcePrefix := toHttpHeaderKey(http.BCE_USER_METADATA_PREFIX) for k, v := range headers { if strings.Index(k, bcePrefix) == 0 { if result.UserMeta == nil { result.UserMeta = make(map[string]string) } result.UserMeta[k[len(bcePrefix):]] = v } } if val, ok := headers[toHttpHeaderKey(http.BCE_OBJECT_TYPE)]; ok { result.ObjectType = val } if val, ok := headers[toHttpHeaderKey(http.BCE_NEXT_APPEND_OFFSET)]; ok { result.NextAppendOffset = val } defer func() { resp.Body().Close() }() return result, nil } // SelectObject - select the object content // // PARAMS: // - cli: the client agent which can perform sending request // - bucket: the bucket name of the object // - object: the name of the object // - args: the optional arguments to perform the select operation // RETURNS: // - *SelectObjectResult: the output select content result of the object // - error: nil if ok otherwise the specific error func SelectObject(cli bce.Client, bucket, object string, args *SelectObjectArgs) (*SelectObjectResult, error) { req := &bce.BceRequest{} req.SetUri(getObjectUri(bucket, object)) req.SetMethod(http.POST) req.SetParam("select", "") req.SetParam("type", args.SelectType) jsonBytes, jsonErr := json.Marshal(args) if jsonErr != nil { return nil, jsonErr } body, err := bce.NewBodyFromBytes(jsonBytes) if err != nil { return nil, err } req.SetBody(body) // Send request and get the result resp := &bce.BceResponse{} if err := SendRequest(cli, req, resp); err != nil { return nil, err } if resp.IsFail() { return nil, resp.ServiceError() } result := &SelectObjectResult{} result.Body = resp.Body() return result, nil } // FetchObject - fetch the object by the given url and store it to a bucket // // PARAMS: // - cli: the client agent which can perform sending request // - bucket: the bucket name to store the object // - object: the name of the object to be stored // - source: the source url to fetch // - args: the optional arguments to perform the fetch operation // RETURNS: // - *FetchObjectArgs: the result of this api // - error: nil if ok otherwise the specific error func FetchObject(cli bce.Client, bucket, object, source string, args *FetchObjectArgs) (*FetchObjectResult, error) { req := &bce.BceRequest{} req.SetUri(getObjectUri(bucket, object)) req.SetMethod(http.POST) req.SetParam("fetch", "") if len(source) == 0 { return nil, bce.NewBceClientError("invalid fetch source value: " + source) } req.SetHeader(http.BCE_PREFIX+"fetch-source", source) // Optional arguments settings if args != nil { if validFetchMode(args.FetchMode) { req.SetHeader(http.BCE_PREFIX+"fetch-mode", args.FetchMode) } else { if len(args.FetchMode) != 0 { return nil, bce.NewBceClientError("invalid fetch mode value: " + args.FetchMode) } } if validStorageClass(args.StorageClass) { req.SetHeader(http.BCE_STORAGE_CLASS, args.StorageClass) } else { if len(args.StorageClass) != 0 { return nil, bce.NewBceClientError("invalid storage class value: " + args.StorageClass) } } } // Send request and get the result resp := &bce.BceResponse{} if err := SendRequest(cli, req, resp); err != nil { return nil, err } if resp.IsFail() { return nil, resp.ServiceError() } jsonBody := &FetchObjectResult{} if err := resp.ParseJsonBody(jsonBody); err != nil { return nil, err } return jsonBody, nil } // AppendObject - append the given content to a new or existed object which is appendable // // PARAMS: // - cli: the client agent which can perform sending request // - bucket: the bucket name of the object // - object: the name of the object // - content: the content to be appended // - args: the optional arguments to perform the append operation // RETURNS: // - *AppendObjectResult: the result status for this api // - error: nil if ok otherwise the specific error func AppendObject(cli bce.Client, bucket, object string, content *bce.Body, args *AppendObjectArgs) (*AppendObjectResult, error) { req := &bce.BceRequest{} req.SetUri(getObjectUri(bucket, object)) req.SetMethod(http.POST) req.SetParam("append", "") if content == nil { return nil, bce.NewBceClientError("AppendObject body should not be emtpy") } if content.Size() >= THRESHOLD_100_CONTINUE { req.SetHeader("Expect", "100-continue") } req.SetBody(content) // Optional arguments settings if args != nil { if args.Offset < 0 { return nil, bce.NewBceClientError( fmt.Sprintf("invalid append offset value: %d", args.Offset)) } if args.Offset > 0 { req.SetParam("offset", fmt.Sprintf("%d", args.Offset)) } setOptionalNullHeaders(req, map[string]string{ http.CACHE_CONTROL: args.CacheControl, http.CONTENT_DISPOSITION: args.ContentDisposition, http.CONTENT_MD5: args.ContentMD5, http.CONTENT_TYPE: args.ContentType, http.EXPIRES: args.Expires, http.BCE_CONTENT_SHA256: args.ContentSha256, http.BCE_CONTENT_CRC32: args.ContentCrc32, }) if validStorageClass(args.StorageClass) { req.SetHeader(http.BCE_STORAGE_CLASS, args.StorageClass) } else { if len(args.StorageClass) != 0 { return nil, bce.NewBceClientError("invalid storage class value: " + args.StorageClass) } } if err := setUserMetadata(req, args.UserMeta); err != nil { return nil, err } //set traffic-limit if args.TrafficLimit > 0 { if args.TrafficLimit > TRAFFIC_LIMIT_MAX || args.TrafficLimit < TRAFFIC_LIMIT_MIN { return nil, bce.NewBceClientError(fmt.Sprintf("TrafficLimit must between %d ~ %d, current value:%d", TRAFFIC_LIMIT_MIN, TRAFFIC_LIMIT_MAX, args.TrafficLimit)) } req.SetHeader(http.BCE_TRAFFIC_LIMIT, fmt.Sprintf("%d", args.TrafficLimit)) } } // Send request and get the result resp := &bce.BceResponse{} if err := SendRequest(cli, req, resp); err != nil { return nil, err } if resp.IsFail() { return nil, resp.ServiceError() } headers := resp.Headers() result := &AppendObjectResult{} if val, ok := headers[http.CONTENT_MD5]; ok { result.ContentMD5 = val } if val, ok := headers[toHttpHeaderKey(http.BCE_NEXT_APPEND_OFFSET)]; ok { nextOffset, offsetErr := strconv.ParseInt(val, 10, 64) if offsetErr != nil { nextOffset = content.Size() } result.NextAppendOffset = nextOffset } else { result.NextAppendOffset = content.Size() } if val, ok := headers[toHttpHeaderKey(http.BCE_CONTENT_CRC32)]; ok { result.ContentCrc32 = val } if val, ok := headers[http.ETAG]; ok { result.ETag = strings.Trim(val, "\"") } defer func() { resp.Body().Close() }() return result, nil } // DeleteObject - delete the given object // // PARAMS: // - cli: the client agent which can perform sending request // - bucket: the bucket name of the object to be deleted // - object: the name of the object // RETURNS: // - error: nil if ok otherwise the specific error func DeleteObject(cli bce.Client, bucket, object string) error { req := &bce.BceRequest{} req.SetUri(getObjectUri(bucket, object)) req.SetMethod(http.DELETE) resp := &bce.BceResponse{} if err := SendRequest(cli, req, resp); err != nil { return err } if resp.IsFail() { return resp.ServiceError() } defer func() { resp.Body().Close() }() return nil } // DeleteMultipleObjects - delete the given objects within a single http request // // PARAMS: // - cli: the client agent which can perform sending request // - bucket: the bucket name of the objects to be deleted // - objectListStream: the objects list to be delete with json format // RETURNS: // - *DeleteMultipleObjectsResult: the objects failed to delete // - error: nil if ok otherwise the specific error func DeleteMultipleObjects(cli bce.Client, bucket string, objectListStream *bce.Body) (*DeleteMultipleObjectsResult, error) { req := &bce.BceRequest{} req.SetUri(getBucketUri(bucket)) req.SetMethod(http.POST) req.SetParam("delete", "") req.SetHeader(http.CONTENT_TYPE, "application/json; charset=utf-8") if objectListStream == nil { return nil, bce.NewBceClientError("DeleteMultipleObjects body should not be emtpy") } if objectListStream.Size() >= THRESHOLD_100_CONTINUE { req.SetHeader("Expect", "100-continue") } req.SetBody(objectListStream) resp := &bce.BceResponse{} if err := SendRequest(cli, req, resp); err != nil { return nil, err } if resp.IsFail() { return nil, resp.ServiceError() } jsonBody := &DeleteMultipleObjectsResult{} if err := resp.ParseJsonBody(jsonBody); err != nil { return nil, err } return jsonBody, nil } // GeneratePresignedUrl - generate an authorization url with expire time and optional arguments // // PARAMS: // - conf: the client configuration // - signer: the client signer object to generate the authorization string // - bucket: the target bucket name // - object: the target object name // - expire: expire time in seconds // - method: optional sign method, default is GET // - headers: optional sign headers, default just set the Host // - params: optional sign params, default is empty // RETURNS: // - string: the presigned url with authorization string func GeneratePresignedUrl(conf *bce.BceClientConfiguration, signer auth.Signer, bucket, object string, expire int, method string, headers, params map[string]string) string { return GeneratePresignedUrlInternal(conf, signer, bucket, object, expire, method, headers, params, false) } func GeneratePresignedUrlPathStyle(conf *bce.BceClientConfiguration, signer auth.Signer, bucket, object string, expire int, method string, headers, params map[string]string) string { return GeneratePresignedUrlInternal(conf, signer, bucket, object, expire, method, headers, params, true) } func GeneratePresignedUrlInternal(conf *bce.BceClientConfiguration, signer auth.Signer, bucket, object string, expire int, method string, headers, params map[string]string, path_style bool) string { req := &bce.BceRequest{} // Set basic arguments if len(method) == 0 { method = http.GET } req.SetMethod(method) req.SetEndpoint(conf.Endpoint) if req.Protocol() == "" { req.SetProtocol(bce.DEFAULT_PROTOCOL) } domain := req.Host() if pos := strings.Index(domain, ":"); pos != -1 { domain = domain[:pos] } if path_style { req.SetUri(getObjectUri(bucket, object)) if conf.CnameEnabled || isCnameLikeHost(conf.Endpoint) { req.SetUri(getCnameUri(req.Uri())) } } else { if len(bucket) != 0 && net.ParseIP(domain) == nil { // not use an IP as the endpoint by client req.SetUri(bce.URI_PREFIX + object) if !conf.CnameEnabled && !isCnameLikeHost(conf.Endpoint) { req.SetHost(bucket + "." + req.Host()) } } else { req.SetUri(getObjectUri(bucket, object)) if conf.CnameEnabled || isCnameLikeHost(conf.Endpoint) { req.SetUri(getCnameUri(req.Uri())) } } } // Set headers and params if given. req.SetHeader(http.HOST, req.Host()) if headers != nil { for k, v := range headers { req.SetHeader(k, v) } } if params != nil { for k, v := range params { req.SetParam(k, v) } } // Copy one SignOptions object to rewrite it. option := *conf.SignOption if expire != 0 { option.ExpireSeconds = expire } if conf.Credentials.SessionToken != "" { req.SetParam(http.BCE_SECURITY_TOKEN, conf.Credentials.SessionToken) } // Generate the authorization string and return the signed url. signer.Sign(&req.Request, conf.Credentials, &option) req.SetParam("authorization", req.Header(http.AUTHORIZATION)) return fmt.Sprintf("%s://%s%s?%s", req.Protocol(), req.Host(), util.UriEncode(req.Uri(), false), req.QueryString()) } // PutObjectAcl - set the ACL of the given object // // PARAMS: // - cli: the client agent which can perform sending request // - bucket: the bucket name // - object: the object name // - cannedAcl: support private and public-read // - grantRead: user id list // - grantFullControl: user id list // - aclBody: the acl file body // RETURNS: // - error: nil if success otherwise the specific error func PutObjectAcl(cli bce.Client, bucket, object, cannedAcl string, grantRead, grantFullControl []string, aclBody *bce.Body) error { req := &bce.BceRequest{} req.SetUri(getObjectUri(bucket, object)) req.SetMethod(http.PUT) req.SetParam("acl", "") // Joiner for generate the user id list string for grant acl header joiner := func(ids []string) string { for i := range ids { ids[i] = "id=\"" + ids[i] + "\"" } return strings.Join(ids, ",") } // Choose a acl setting method methods := 0 if len(cannedAcl) != 0 { methods += 1 if validCannedAcl(cannedAcl) { req.SetHeader(http.BCE_ACL, cannedAcl) } } if len(grantRead) != 0 { methods += 1 req.SetHeader(http.BCE_GRANT_READ, joiner(grantRead)) } if len(grantFullControl) != 0 { methods += 1 req.SetHeader(http.BCE_GRANT_FULL_CONTROL, joiner(grantFullControl)) } if aclBody != nil { methods += 1 req.SetHeader(http.CONTENT_TYPE, bce.DEFAULT_CONTENT_TYPE) req.SetBody(aclBody) } if methods != 1 { return bce.NewBceClientError("BOS only support one acl setting method at the same time") } // Do sending request resp := &bce.BceResponse{} if err := SendRequest(cli, req, resp); err != nil { return err } if resp.IsFail() { return resp.ServiceError() } defer func() { resp.Body().Close() }() return nil } // GetObjectAcl - get the ACL of the given object // // PARAMS: // - cli: the client agent which can perform sending request // - bucket: the bucket name // - object: the object name // RETURNS: // - result: the object acl result object // - error: nil if success otherwise the specific error func GetObjectAcl(cli bce.Client, bucket, object string) (*GetObjectAclResult, error) { req := &bce.BceRequest{} req.SetUri(getObjectUri(bucket, object)) req.SetMethod(http.GET) req.SetParam("acl", "") resp := &bce.BceResponse{} if err := SendRequest(cli, req, resp); err != nil { return nil, err } if resp.IsFail() { return nil, resp.ServiceError() } result := &GetObjectAclResult{} if err := resp.ParseJsonBody(result); err != nil { return nil, err } return result, nil } // DeleteObjectAcl - delete the ACL of the given object // // PARAMS: // - cli: the client agent which can perform sending request // - bucket: the bucket name // - object: the object name // RETURNS: // - error: nil if success otherwise the specific error func DeleteObjectAcl(cli bce.Client, bucket, object string) error { req := &bce.BceRequest{} req.SetUri(getObjectUri(bucket, object)) req.SetMethod(http.DELETE) req.SetParam("acl", "") resp := &bce.BceResponse{} if err := SendRequest(cli, req, resp); err != nil { return err } if resp.IsFail() { return resp.ServiceError() } defer func() { resp.Body().Close() }() return nil } // RestoreObject - restore the archive object // // PARAMS: // - cli: the client agent which can perform sending request // - bucket: the bucket name // - object: the object name // - args: the restore args // RETURNS: // - error: nil if success otherwise the specific error func RestoreObject(cli bce.Client, bucket string, object string, args ArchiveRestoreArgs) error { req := &bce.BceRequest{} req.SetUri(getObjectUri(bucket, object)) req.SetParam("restore", "") req.SetMethod(http.POST) req.SetHeader(http.BCE_RESTORE_DAYS, strconv.Itoa(args.RestoreDays)) req.SetHeader(http.BCE_RESTORE_TIER, args.RestoreTier) resp := &bce.BceResponse{} if err := SendRequest(cli, req, resp); err != nil { return err } if resp.IsFail() { return resp.ServiceError() } return nil } // PutObjectSymlink - put the object from the string or the stream // // PARAMS: // - cli: the client agent which can perform sending request // - bucket: the bucket name of the object // - object: the name of the object // - symlinkKey: the name of the symlink // - symlinkArgs: the optional arguments of this api // RETURNS: // - error: nil if ok otherwise the specific error func PutObjectSymlink(cli bce.Client, bucket string, object string, symlinkKey string, symlinkArgs *PutSymlinkArgs) error { req := &bce.BceRequest{} req.SetUri(getObjectUri(bucket, symlinkKey)) req.SetParam("symlink", "") req.SetMethod(http.PUT) if symlinkArgs != nil { if len(symlinkArgs.ForbidOverwrite) != 0 { if !validForbidOverwrite(symlinkArgs.ForbidOverwrite) { return bce.NewBceClientError("invalid forbid overwrite val," + symlinkArgs.ForbidOverwrite) } req.SetHeader(http.BCE_FORBID_OVERWRITE, symlinkArgs.ForbidOverwrite) } if len(symlinkArgs.StorageClass) != 0 { if !validStorageClass(symlinkArgs.StorageClass) { return bce.NewBceClientError("invalid storage class val," + symlinkArgs.StorageClass) } if symlinkArgs.StorageClass == STORAGE_CLASS_ARCHIVE { return bce.NewBceClientError("archive storage class not support") } req.SetHeader(http.BCE_STORAGE_CLASS, symlinkArgs.StorageClass) } if len(symlinkArgs.UserMeta) != 0 { if err := setUserMetadata(req, symlinkArgs.UserMeta); err != nil { return err } } if len(symlinkArgs.SymlinkBucket) != 0 { req.SetHeader(http.BCE_SYMLINK_BUCKET, symlinkArgs.SymlinkBucket) } } req.SetHeader(http.BCE_SYMLINK_TARGET, object) resp := &bce.BceResponse{} if err := SendRequest(cli, req, resp); err != nil { return err } if resp.IsFail() { return resp.ServiceError() } defer func() { resp.Body().Close() }() return nil } // PutObjectSymlink - put the object from the string or the stream // // PARAMS: // - cli: the client agent which can perform sending request // - bucket: the bucket name of the object // - symlinkKey: the name of the symlink // RETURNS: // - string: the name of the target object // - error: nil if ok otherwise the specific error func GetObjectSymlink(cli bce.Client, bucket string, symlinkKey string) (string, error) { req := &bce.BceRequest{} req.SetUri(getObjectUri(bucket, symlinkKey)) req.SetParam("symlink", "") req.SetMethod(http.GET) resp := &bce.BceResponse{} if err := SendRequest(cli, req, resp); err != nil { return "", err } if resp.IsFail() { return "", resp.ServiceError() } defer func() { resp.Body().Close() }() if resp.Header(http.BCE_SYMLINK_BUCKET) != "" { result := BOS_CONFIG_PREFIX + resp.Header(http.BCE_SYMLINK_BUCKET) + "/" + resp.Header(http.BCE_SYMLINK_TARGET) return result, nil } return resp.Header(http.BCE_SYMLINK_TARGET), nil }