| 404 | } |
| 405 | |
| 406 | func (rw *Azure) append(ctx context.Context, src []byte, name string) error { |
| 407 | appendBlobClient := rw.containerClient.NewBlockBlobClient(name) |
| 408 | |
| 409 | // These helper functions convert a binary block ID to a base-64 string and vice versa |
| 410 | // NOTE: The blockID must be <= 64 bytes and ALL blockIDs for the block must be the same length |
| 411 | blockIDBinaryToBase64 := func(blockID []byte) string { return base64.StdEncoding.EncodeToString(blockID) } |
| 412 | |
| 413 | blockIDIntToBase64 := func(blockID int) string { |
| 414 | binaryBlockID := (&[64]byte{})[:] |
| 415 | binary.LittleEndian.PutUint32(binaryBlockID, uint32(blockID)) |
| 416 | return blockIDBinaryToBase64(binaryBlockID) |
| 417 | } |
| 418 | |
| 419 | l, err := appendBlobClient.GetBlockList(ctx, blockblob.BlockListTypeAll, &blockblob.GetBlockListOptions{}) |
| 420 | if err != nil { |
| 421 | return err |
| 422 | } |
| 423 | |
| 424 | // generate the next block id |
| 425 | id := blockIDIntToBase64(len(l.CommittedBlocks) + 1) |
| 426 | |
| 427 | _, err = appendBlobClient.StageBlock(ctx, id, streaming.NopCloser(bytes.NewReader(src)), &blockblob.StageBlockOptions{}) |
| 428 | if err != nil { |
| 429 | return err |
| 430 | } |
| 431 | |
| 432 | base64BlockIDs := make([]string, len(l.CommittedBlocks)+1) |
| 433 | for i := 0; i < len(l.CommittedBlocks); i++ { |
| 434 | base64BlockIDs[i] = *l.CommittedBlocks[i].Name |
| 435 | } |
| 436 | |
| 437 | base64BlockIDs[len(l.CommittedBlocks)] = id |
| 438 | |
| 439 | // After all the blocks are uploaded, atomically commit them to the blob. |
| 440 | _, err = appendBlobClient.CommitBlockList(ctx, base64BlockIDs, &blockblob.CommitBlockListOptions{}) |
| 441 | if err != nil { |
| 442 | return err |
| 443 | } |
| 444 | return nil |
| 445 | } |
| 446 | |
| 447 | func (rw *Azure) writer(ctx context.Context, src io.Reader, name string) error { |
| 448 | blobClient := rw.containerClient.NewBlockBlobClient(name) |