1package azblob 2 3import ( 4 "context" 5 "io" 6 "net/url" 7 8 "encoding/base64" 9 "encoding/binary" 10 11 "github.com/Azure/azure-pipeline-go/pipeline" 12) 13 14const ( 15 // BlockBlobMaxUploadBlobBytes indicates the maximum number of bytes that can be sent in a call to Upload. 16 BlockBlobMaxUploadBlobBytes = 256 * 1024 * 1024 // 256MB 17 18 // BlockBlobMaxStageBlockBytes indicates the maximum number of bytes that can be sent in a call to StageBlock. 19 BlockBlobMaxStageBlockBytes = 100 * 1024 * 1024 // 100MB 20 21 // BlockBlobMaxBlocks indicates the maximum number of blocks allowed in a block blob. 22 BlockBlobMaxBlocks = 50000 23) 24 25// BlockBlobURL defines a set of operations applicable to block blobs. 26type BlockBlobURL struct { 27 BlobURL 28 bbClient blockBlobClient 29} 30 31// NewBlockBlobURL creates a BlockBlobURL object using the specified URL and request policy pipeline. 32func NewBlockBlobURL(url url.URL, p pipeline.Pipeline) BlockBlobURL { 33 blobClient := newBlobClient(url, p) 34 bbClient := newBlockBlobClient(url, p) 35 return BlockBlobURL{BlobURL: BlobURL{blobClient: blobClient}, bbClient: bbClient} 36} 37 38// WithPipeline creates a new BlockBlobURL object identical to the source but with the specific request policy pipeline. 39func (bb BlockBlobURL) WithPipeline(p pipeline.Pipeline) BlockBlobURL { 40 return NewBlockBlobURL(bb.blobClient.URL(), p) 41} 42 43// WithSnapshot creates a new BlockBlobURL object identical to the source but with the specified snapshot timestamp. 44// Pass "" to remove the snapshot returning a URL to the base blob. 45func (bb BlockBlobURL) WithSnapshot(snapshot string) BlockBlobURL { 46 p := NewBlobURLParts(bb.URL()) 47 p.Snapshot = snapshot 48 return NewBlockBlobURL(p.URL(), bb.blobClient.Pipeline()) 49} 50 51// Upload creates a new block blob or overwrites an existing block blob. 52// Updating an existing block blob overwrites any existing metadata on the blob. Partial updates are not 53// supported with Upload; the content of the existing blob is overwritten with the new content. To 54// perform a partial update of a block blob, use StageBlock and CommitBlockList. 55// This method panics if the stream is not at position 0. 56// Note that the http client closes the body stream after the request is sent to the service. 57// For more information, see https://docs.microsoft.com/rest/api/storageservices/put-blob. 58func (bb BlockBlobURL) Upload(ctx context.Context, body io.ReadSeeker, h BlobHTTPHeaders, metadata Metadata, ac BlobAccessConditions) (*BlockBlobUploadResponse, error) { 59 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers() 60 count, err := validateSeekableStreamAt0AndGetCount(body) 61 if err != nil { 62 return nil, err 63 } 64 return bb.bbClient.Upload(ctx, body, count, nil, 65 &h.ContentType, &h.ContentEncoding, &h.ContentLanguage, h.ContentMD5, 66 &h.CacheControl, metadata, ac.LeaseAccessConditions.pointers(), 67 &h.ContentDisposition, ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, 68 nil) 69} 70 71// StageBlock uploads the specified block to the block blob's "staging area" to be later committed by a call to CommitBlockList. 72// Note that the http client closes the body stream after the request is sent to the service. 73// For more information, see https://docs.microsoft.com/rest/api/storageservices/put-block. 74func (bb BlockBlobURL) StageBlock(ctx context.Context, base64BlockID string, body io.ReadSeeker, ac LeaseAccessConditions, transactionalMD5 []byte) (*BlockBlobStageBlockResponse, error) { 75 count, err := validateSeekableStreamAt0AndGetCount(body) 76 if err != nil { 77 return nil, err 78 } 79 return bb.bbClient.StageBlock(ctx, base64BlockID, count, body, transactionalMD5, nil, ac.pointers(), nil) 80} 81 82// StageBlockFromURL copies the specified block from a source URL to the block blob's "staging area" to be later committed by a call to CommitBlockList. 83// If count is CountToEnd (0), then data is read from specified offset to the end. 84// For more information, see https://docs.microsoft.com/en-us/rest/api/storageservices/put-block-from-url. 85func (bb BlockBlobURL) StageBlockFromURL(ctx context.Context, base64BlockID string, sourceURL url.URL, offset int64, count int64, destinationAccessConditions LeaseAccessConditions, sourceAccessConditions ModifiedAccessConditions) (*BlockBlobStageBlockFromURLResponse, error) { 86 sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatchETag, sourceIfNoneMatchETag := sourceAccessConditions.pointers() 87 return bb.bbClient.StageBlockFromURL(ctx, base64BlockID, 0, sourceURL.String(), httpRange{offset: offset, count: count}.pointers(), nil, nil, destinationAccessConditions.pointers(), sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatchETag, sourceIfNoneMatchETag, nil) 88} 89 90// CommitBlockList writes a blob by specifying the list of block IDs that make up the blob. 91// In order to be written as part of a blob, a block must have been successfully written 92// to the server in a prior PutBlock operation. You can call PutBlockList to update a blob 93// by uploading only those blocks that have changed, then committing the new and existing 94// blocks together. Any blocks not specified in the block list and permanently deleted. 95// For more information, see https://docs.microsoft.com/rest/api/storageservices/put-block-list. 96func (bb BlockBlobURL) CommitBlockList(ctx context.Context, base64BlockIDs []string, h BlobHTTPHeaders, 97 metadata Metadata, ac BlobAccessConditions) (*BlockBlobCommitBlockListResponse, error) { 98 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers() 99 return bb.bbClient.CommitBlockList(ctx, BlockLookupList{Latest: base64BlockIDs}, nil, 100 &h.CacheControl, &h.ContentType, &h.ContentEncoding, &h.ContentLanguage, h.ContentMD5, 101 metadata, ac.LeaseAccessConditions.pointers(), &h.ContentDisposition, 102 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil) 103} 104 105// GetBlockList returns the list of blocks that have been uploaded as part of a block blob using the specified block list filter. 106// For more information, see https://docs.microsoft.com/rest/api/storageservices/get-block-list. 107func (bb BlockBlobURL) GetBlockList(ctx context.Context, listType BlockListType, ac LeaseAccessConditions) (*BlockList, error) { 108 return bb.bbClient.GetBlockList(ctx, listType, nil, nil, ac.pointers(), nil) 109} 110 111////////////////////////////////////////////////////////////////////////////////////////////////////////////// 112 113type BlockID [64]byte 114 115func (blockID BlockID) ToBase64() string { 116 return base64.StdEncoding.EncodeToString(blockID[:]) 117} 118 119func (blockID *BlockID) FromBase64(s string) error { 120 *blockID = BlockID{} // Zero out the block ID 121 _, err := base64.StdEncoding.Decode(blockID[:], ([]byte)(s)) 122 return err 123} 124 125////////////////////////////////////////////////////////////////////////////////////////////////////////////// 126 127type uuidBlockID BlockID 128 129func (ubi uuidBlockID) UUID() uuid { 130 u := uuid{} 131 copy(u[:], ubi[:len(u)]) 132 return u 133} 134 135func (ubi uuidBlockID) Number() uint32 { 136 return binary.BigEndian.Uint32(ubi[len(uuid{}):]) 137} 138 139func newUuidBlockID(u uuid) uuidBlockID { 140 ubi := uuidBlockID{} // Create a new uuidBlockID 141 copy(ubi[:len(u)], u[:]) // Copy the specified UUID into it 142 // Block number defaults to 0 143 return ubi 144} 145 146func (ubi *uuidBlockID) SetUUID(u uuid) *uuidBlockID { 147 copy(ubi[:len(u)], u[:]) 148 return ubi 149} 150 151func (ubi uuidBlockID) WithBlockNumber(blockNumber uint32) uuidBlockID { 152 binary.BigEndian.PutUint32(ubi[len(uuid{}):], blockNumber) // Put block number after UUID 153 return ubi // Return the passed-in copy 154} 155 156func (ubi uuidBlockID) ToBase64() string { 157 return BlockID(ubi).ToBase64() 158} 159 160func (ubi *uuidBlockID) FromBase64(s string) error { 161 return (*BlockID)(ubi).FromBase64(s) 162} 163