PutObjectAnnotation creates or overwrites a named annotation on an object version. The payload (1 byte to 1 MiB) is streamed directly from the supplied ReadSeeker: its size is taken from a seek to the end, so the body is sent with an exact Content-Length and never buffered in memory. It returns the
(ctx context.Context, bucketName, objectName, annotationName string, payload io.ReadSeeker, opts PutObjectAnnotationOptions)
| 117 | // an exact Content-Length and never buffered in memory. It returns the |
| 118 | // annotation's ETag. The parent object's ETag is never modified. |
| 119 | func (c *Client) PutObjectAnnotation(ctx context.Context, bucketName, objectName, annotationName string, payload io.ReadSeeker, opts PutObjectAnnotationOptions) (string, error) { |
| 120 | if err := s3utils.CheckValidBucketName(bucketName); err != nil { |
| 121 | return "", err |
| 122 | } |
| 123 | if err := s3utils.CheckValidObjectName(objectName); err != nil { |
| 124 | return "", err |
| 125 | } |
| 126 | if err := validateAnnotationName(annotationName); err != nil { |
| 127 | return "", err |
| 128 | } |
| 129 | if payload == nil { |
| 130 | return "", errInvalidArgument("annotation payload must not be nil") |
| 131 | } |
| 132 | |
| 133 | // Derive the payload size from the seeker and enforce the limits before |
| 134 | // uploading; the server reads the body raw, so it is sent with UNSIGNED-PAYLOAD |
| 135 | // (no streaming-chunked signature) and streamed without buffering. |
| 136 | size, err := payload.Seek(0, io.SeekEnd) |
| 137 | if err != nil { |
| 138 | return "", err |
| 139 | } |
| 140 | if _, err := payload.Seek(0, io.SeekStart); err != nil { |
| 141 | return "", err |
| 142 | } |
| 143 | if size == 0 { |
| 144 | return "", errInvalidArgument("annotation payload must be at least 1 byte") |
| 145 | } |
| 146 | if size > maxAnnotationPayloadBytes { |
| 147 | return "", errInvalidArgument("annotation payload exceeds the 1 MiB maximum") |
| 148 | } |
| 149 | |
| 150 | headers := make(http.Header) |
| 151 | if opts.IfMatch != "" { |
| 152 | headers.Set(amzObjectIfMatchHeader, opts.IfMatch) |
| 153 | } |
| 154 | |
| 155 | resp, err := c.executeMethod(ctx, http.MethodPut, requestMetadata{ |
| 156 | bucketName: bucketName, |
| 157 | objectName: objectName, |
| 158 | queryValues: annotationQueryValues(annotationName, opts.VersionID), |
| 159 | contentBody: payload, |
| 160 | contentLength: size, |
| 161 | customHeader: headers, |
| 162 | }) |
| 163 | defer closeResponse(resp) |
| 164 | if err != nil { |
| 165 | return "", err |
| 166 | } |
| 167 | if resp != nil && resp.StatusCode != http.StatusOK { |
| 168 | return "", httpRespToErrorResponse(resp, bucketName, objectName) |
| 169 | } |
| 170 | return trimEtag(resp.Header.Get("ETag")), nil |
| 171 | } |
| 172 | |
| 173 | // GetObjectAnnotation returns the payload of a single named annotation as a |
| 174 | // stream. The returned ReadCloser is the response body; the caller must Close it |
no test coverage detected