/* * MinIO Go Library for Amazon S3 Compatible Cloud Storage * Copyright 2020 MinIO, 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. */ package minio import ( "context" "encoding/xml" "errors" "net/http" "net/url" "strconv" "time" "github.com/minio/minio-go/v7/pkg/encrypt" "github.com/minio/minio-go/v7/pkg/s3utils" ) // ObjectAttributesOptions are options used for the GetObjectAttributes API // // - MaxParts // How many parts the caller wants to be returned (default: 1000) // // - VersionID // The object version you want to attributes for // // - PartNumberMarker // the listing will start AFTER the part matching PartNumberMarker // // - ServerSideEncryption // The server-side encryption algorithm used when storing this object in Minio type ObjectAttributesOptions struct { MaxParts int VersionID string PartNumberMarker int ServerSideEncryption encrypt.ServerSide } // ObjectAttributes is the response object returned by the GetObjectAttributes API // // - VersionID // The object version // // - LastModified // The last time the object was modified // // - ObjectAttributesResponse // Contains more information about the object type ObjectAttributes struct { VersionID string LastModified time.Time ObjectAttributesResponse } // ObjectAttributesResponse contains details returned by the GetObjectAttributes API // // Noteworthy fields: // // - ObjectParts.PartsCount // Contains the total part count for the object (not the current response) // // - ObjectParts.PartNumberMarker // Pagination of parts will begin at (but not include) PartNumberMarker // // - ObjectParts.NextPartNumberMarket // The next PartNumberMarker to be used in order to continue pagination // // - ObjectParts.IsTruncated // Indicates if the last part is included in the request (does not check if parts are missing from the start of the list, ONLY the end) // // - ObjectParts.MaxParts // Reflects the MaxParts used by the caller or the default MaxParts value of the API type ObjectAttributesResponse struct { ETag string `xml:",omitempty"` StorageClass string ObjectSize int Checksum struct { ChecksumCRC32 string `xml:",omitempty"` ChecksumCRC32C string `xml:",omitempty"` ChecksumSHA1 string `xml:",omitempty"` ChecksumSHA256 string `xml:",omitempty"` } ObjectParts struct { PartsCount int PartNumberMarker int NextPartNumberMarker int MaxParts int IsTruncated bool Parts []*ObjectAttributePart `xml:"Part"` } } // ObjectAttributePart is used by ObjectAttributesResponse to describe an object part type ObjectAttributePart struct { ChecksumCRC32 string `xml:",omitempty"` ChecksumCRC32C string `xml:",omitempty"` ChecksumSHA1 string `xml:",omitempty"` ChecksumSHA256 string `xml:",omitempty"` PartNumber int Size int } func (o *ObjectAttributes) parseResponse(resp *http.Response) (err error) { mod, err := parseRFC7231Time(resp.Header.Get("Last-Modified")) if err != nil { return err } o.LastModified = mod o.VersionID = resp.Header.Get(amzVersionID) response := new(ObjectAttributesResponse) if err := xml.NewDecoder(resp.Body).Decode(response); err != nil { return err } o.ObjectAttributesResponse = *response return } // GetObjectAttributes API combines HeadObject and ListParts. // More details on usage can be found in the documentation for ObjectAttributesOptions{} func (c *Client) GetObjectAttributes(ctx context.Context, bucketName, objectName string, opts ObjectAttributesOptions) (*ObjectAttributes, error) { if err := s3utils.CheckValidBucketName(bucketName); err != nil { return nil, err } if err := s3utils.CheckValidObjectName(objectName); err != nil { return nil, err } urlValues := make(url.Values) urlValues.Add("attributes", "") if opts.VersionID != "" { urlValues.Add("versionId", opts.VersionID) } headers := make(http.Header) headers.Set(amzObjectAttributes, GetObjectAttributesTags) if opts.PartNumberMarker > 0 { headers.Set(amzPartNumberMarker, strconv.Itoa(opts.PartNumberMarker)) } if opts.MaxParts > 0 { headers.Set(amzMaxParts, strconv.Itoa(opts.MaxParts)) } else { headers.Set(amzMaxParts, strconv.Itoa(GetObjectAttributesMaxParts)) } if opts.ServerSideEncryption != nil { opts.ServerSideEncryption.Marshal(headers) } resp, err := c.executeMethod(ctx, http.MethodGet, requestMetadata{ bucketName: bucketName, objectName: objectName, queryValues: urlValues, contentSHA256Hex: emptySHA256Hex, customHeader: headers, }) if err != nil { return nil, err } defer closeResponse(resp) hasEtag := resp.Header.Get(ETag) if hasEtag != "" { return nil, errors.New("getObjectAttributes is not supported by the current endpoint version") } if resp.StatusCode != http.StatusOK { ER := new(ErrorResponse) if err := xml.NewDecoder(resp.Body).Decode(ER); err != nil { return nil, err } return nil, *ER } OA := new(ObjectAttributes) err = OA.parseResponse(resp) if err != nil { return nil, err } return OA, nil }