/// <summary>Constructs a BlobLogBase object.</summary>
 /// <param name="container">Indicates the container where log blobs should be placed.</param>
 /// <param name="extension">Indicates the extension (initial period is optional) of the log blob.</param>
 /// <param name="mimeType">Indicates the mime type of the log blob.</param>
 /// <param name="header">Indicates the header (first block) of the log blob. Pass 'null' if you do not want a header.</param>
 /// <param name="maxEntries">Indicates the maximum number of blocks the blob should have. Old blocks are deleted when going over this limit.</param>
 protected internal BlobLogBase(CloudBlobContainer container, String extension, String mimeType, Byte[] header = null, Int32 maxEntries = 49000)
 {
     m_container = container;
     m_extension = extension.StartsWith(".") ? extension : "." + extension;
     m_info      = new AppendBlobBlockInfo {
         MimeType = mimeType, Header = header, MaxEntries = maxEntries
     };
 }
Exemplo n.º 2
0
        /// <summary>Appends a block to the end of a block blob.</summary>
        /// <param name="blob">The blob to append a block to.</param>
        /// <param name="info">Additional info used when the blob is first being created.</param>
        /// <param name="dataToAppend">The data to append as a block to the end of the blob.</param>
        /// <param name="options">The blob request options.</param>
        /// <param name="operationContext">The operation context.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>A Task indicating when the operation has completed.</returns>
        public static async Task AppendBlockAsync(this CloudBlockBlob blob, AppendBlobBlockInfo info,
                                                  Byte[] dataToAppend, BlobRequestOptions options = null, OperationContext operationContext = null,
                                                  CancellationToken cancellationToken             = default(CancellationToken))
        {
            blob.Properties.ContentType = info.MimeType;
            //blob.Properties.ContentDisposition = "attachment; filename=\"fname.ext\"";

            // NOTE: Put Block ignores access condition and so it always succeeds
            List <String> blockList = new List <String>();

            while (true)
            {
                blockList.Clear();
                AccessCondition accessCondition;
                try {
                    blockList.AddRange((await blob.DownloadBlockListAsync(BlockListingFilter.Committed,
                                                                          null, options, operationContext, cancellationToken).ConfigureAwait(false)).Select(lbi => lbi.Name)); // 404 if blob not found
                    accessCondition = AccessCondition.GenerateIfMatchCondition(blob.Properties.ETag);                                                                          // Write if blob matches what we read
                }
                catch (StorageException se) {
                    if (!se.Matches(HttpStatusCode.NotFound, BlobErrorCodeStrings.BlobNotFound))
                    {
                        throw;
                    }
                    accessCondition = AccessCondition.GenerateIfNoneMatchCondition("*"); // Write if blob doesn't exist
                }
                try {
                    if (blockList.Count == 0) // Blob doesn't exist yet, add header (if specified)
                    // Notify client code that new blob is about to be created
                    {
                        info.OnCreatingBlob(blob);

                        if (info.Header != null) // Write header to new blob
                        {
                            String headerBlockId = Guid.NewGuid().ToString().Encode().ToBase64String();
                            await blob.PutBlockAsync(headerBlockId, new MemoryStream(info.Header), null).ConfigureAwait(false);

                            blockList.Add(headerBlockId);
                        }
                    }
                    // Upload new block & add it's Id to the block list
                    String blockId = Guid.NewGuid().ToString().Encode().ToBase64String();
                    await blob.PutBlockAsync(blockId, new MemoryStream(dataToAppend), null).ConfigureAwait(false);

                    blockList.Add(blockId);

                    // If too many blocks, remove old block (but not the header if it exists)
                    var maxEntries = info.MaxEntries + ((info.Header == null) ? 0 : 1);
                    if (blockList.Count > maxEntries)
                    {
                        blockList.RemoveAt((info.Header == null) ? 0 : 1);
                    }

                    // Upload the new block list
                    await blob.PutBlockListAsync(blockList, AccessCondition.GenerateIfMatchCondition(blob.Properties.ETag),
                                                 options, operationContext, cancellationToken).ConfigureAwait(false); // 409 if blob created behind our back; 400 if block Id doesn't exist (happens in another PC calls PutBlockList after our PutBlock)

                    break;                                                                                            // If successful, we're done; don't retry
                }
                catch (StorageException se) {
                    // Blob got created behind our back, retry
                    if (se.Matches(HttpStatusCode.Conflict, BlobErrorCodeStrings.BlobAlreadyExists))
                    {
                        continue;
                    }

                    // Blob got created or modified behind our back, retry
                    if (se.Matches(HttpStatusCode.PreconditionFailed))
                    {
                        continue;
                    }

                    // Another PC called PutBlockList between our PutBlock & PutBlockList,
                    // our block(s) got destroyed, retry
                    if (se.Matches(HttpStatusCode.BadRequest, BlobErrorCodeStrings.InvalidBlockList))
                    {
                        continue;
                    }
                    throw;
                }
            }
        }