1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
|
package blobs
import (
"context"
"fmt"
"net/http"
"strconv"
"strings"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/validation"
"github.com/tombuildsstuff/giovanni/storage/internal/endpoints"
)
type AppendBlockInput struct {
// A number indicating the byte offset to compare.
// Append Block will succeed only if the append position is equal to this number.
// If it is not, the request will fail with an AppendPositionConditionNotMet
// error (HTTP status code 412 – Precondition Failed)
BlobConditionAppendPosition *int64
// The max length in bytes permitted for the append blob.
// If the Append Block operation would cause the blob to exceed that limit or if the blob size
// is already greater than the value specified in this header, the request will fail with
// an MaxBlobSizeConditionNotMet error (HTTP status code 412 – Precondition Failed).
BlobConditionMaxSize *int64
// The Bytes which should be appended to the end of this Append Blob.
// This can either be nil, which creates an empty blob, or a byte array
Content *[]byte
// An MD5 hash of the block content.
// This hash is used to verify the integrity of the block during transport.
// When this header is specified, the storage service compares the hash of the content
// that has arrived with this header value.
//
// Note that this MD5 hash is not stored with the blob.
// If the two hashes do not match, the operation will fail with error code 400 (Bad Request).
ContentMD5 *string
// Required if the blob has an active lease.
// To perform this operation on a blob with an active lease, specify the valid lease ID for this header.
LeaseID *string
}
type AppendBlockResult struct {
autorest.Response
BlobAppendOffset string
BlobCommittedBlockCount int64
ContentMD5 string
ETag string
LastModified string
}
// AppendBlock commits a new block of data to the end of an existing append blob.
func (client Client) AppendBlock(ctx context.Context, accountName, containerName, blobName string, input AppendBlockInput) (result AppendBlockResult, err error) {
if accountName == "" {
return result, validation.NewError("blobs.Client", "AppendBlock", "`accountName` cannot be an empty string.")
}
if containerName == "" {
return result, validation.NewError("blobs.Client", "AppendBlock", "`containerName` cannot be an empty string.")
}
if strings.ToLower(containerName) != containerName {
return result, validation.NewError("blobs.Client", "AppendBlock", "`containerName` must be a lower-cased string.")
}
if blobName == "" {
return result, validation.NewError("blobs.Client", "AppendBlock", "`blobName` cannot be an empty string.")
}
if input.Content != nil && len(*input.Content) > (4*1024*1024) {
return result, validation.NewError("files.Client", "PutByteRange", "`input.Content` must be at most 4MB.")
}
req, err := client.AppendBlockPreparer(ctx, accountName, containerName, blobName, input)
if err != nil {
err = autorest.NewErrorWithError(err, "blobs.Client", "AppendBlock", nil, "Failure preparing request")
return
}
resp, err := client.AppendBlockSender(req)
if err != nil {
result.Response = autorest.Response{Response: resp}
err = autorest.NewErrorWithError(err, "blobs.Client", "AppendBlock", resp, "Failure sending request")
return
}
result, err = client.AppendBlockResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "blobs.Client", "AppendBlock", resp, "Failure responding to request")
return
}
return
}
// AppendBlockPreparer prepares the AppendBlock request.
func (client Client) AppendBlockPreparer(ctx context.Context, accountName, containerName, blobName string, input AppendBlockInput) (*http.Request, error) {
pathParameters := map[string]interface{}{
"containerName": autorest.Encode("path", containerName),
"blobName": autorest.Encode("path", blobName),
}
queryParameters := map[string]interface{}{
"comp": autorest.Encode("query", "appendblock"),
}
headers := map[string]interface{}{
"x-ms-version": APIVersion,
}
if input.BlobConditionAppendPosition != nil {
headers["x-ms-blob-condition-appendpos"] = *input.BlobConditionAppendPosition
}
if input.BlobConditionMaxSize != nil {
headers["x-ms-blob-condition-maxsize"] = *input.BlobConditionMaxSize
}
if input.ContentMD5 != nil {
headers["x-ms-blob-content-md5"] = *input.ContentMD5
}
if input.LeaseID != nil {
headers["x-ms-lease-id"] = *input.LeaseID
}
if input.Content != nil {
headers["Content-Length"] = int(len(*input.Content))
}
decorators := []autorest.PrepareDecorator{
autorest.AsPut(),
autorest.WithBaseURL(endpoints.GetBlobEndpoint(client.BaseURI, accountName)),
autorest.WithPathParameters("/{containerName}/{blobName}", pathParameters),
autorest.WithQueryParameters(queryParameters),
autorest.WithHeaders(headers),
}
if input.Content != nil {
decorators = append(decorators, autorest.WithBytes(input.Content))
}
preparer := autorest.CreatePreparer(decorators...)
return preparer.Prepare((&http.Request{}).WithContext(ctx))
}
// AppendBlockSender sends the AppendBlock request. The method will close the
// http.Response Body if it receives an error.
func (client Client) AppendBlockSender(req *http.Request) (*http.Response, error) {
return autorest.SendWithSender(client, req,
azure.DoRetryWithRegistration(client.Client))
}
// AppendBlockResponder handles the response to the AppendBlock request. The method always
// closes the http.Response Body.
func (client Client) AppendBlockResponder(resp *http.Response) (result AppendBlockResult, err error) {
if resp != nil && resp.Header != nil {
result.BlobAppendOffset = resp.Header.Get("x-ms-blob-append-offset")
result.ContentMD5 = resp.Header.Get("ETag")
result.ETag = resp.Header.Get("ETag")
result.LastModified = resp.Header.Get("Last-Modified")
if v := resp.Header.Get("x-ms-blob-committed-block-count"); v != "" {
i, innerErr := strconv.Atoi(v)
if innerErr != nil {
err = fmt.Errorf("Error parsing %q as an integer: %s", v, innerErr)
return
}
result.BlobCommittedBlockCount = int64(i)
}
}
err = autorest.Respond(
resp,
client.ByInspecting(),
azure.WithErrorUnlessStatusCode(http.StatusCreated),
autorest.ByClosing())
result.Response = autorest.Response{Response: resp}
return
}
|