Ejemplo n.º 1
0
        /// <summary>
        /// The <see cref="CreateIfNotExistsInternal"/> operation creates a new 0-length
        /// append blob.  If the append blob already exists, the content of
        /// the existing append blob will remain unchanged.  To add content to the append
        /// blob, call the <see cref="AppendBlockAsync"/> operation.
        ///
        /// For more information, see <see href="https://docs.microsoft.com/rest/api/storageservices/put-blob" />.
        /// </summary>
        /// <param name="httpHeaders">
        /// Optional standard HTTP header properties that can be set for the
        /// new append blob.
        /// </param>
        /// <param name="metadata">
        /// Optional custom metadata to set for this append blob.
        /// </param>
        /// <param name="async">
        /// Whether to invoke the operation asynchronously.
        /// </param>
        /// <param name="cancellationToken">
        /// Optional <see cref="CancellationToken"/> to propagate
        /// notifications that the operation should be cancelled.
        /// </param>
        /// <returns>
        /// A <see cref="Response{BlobContentInfo}"/> describing the
        /// newly created append blob.
        /// </returns>
        /// <remarks>
        /// A <see cref="StorageRequestFailedException"/> will be thrown if
        /// a failure occurs.
        /// </remarks>
        private async Task <Response <BlobContentInfo> > CreateIfNotExistsInternal(
            BlobHttpHeaders?httpHeaders,
            Metadata metadata,
            bool async,
            CancellationToken cancellationToken)
        {
            AppendBlobAccessConditions accessConditions = new AppendBlobAccessConditions
            {
                HttpAccessConditions = new HttpAccessConditions
                {
                    IfNoneMatch = new ETag(Constants.Wildcard)
                }
            };

            try
            {
                return(await CreateInternal(
                           httpHeaders,
                           metadata,
                           accessConditions,
                           async,
                           cancellationToken,
                           Constants.Blob.Append.CreateIfNotExistsOperationName)
                       .ConfigureAwait(false));
            }
            catch (StorageRequestFailedException storageRequestFailedException)
                when(storageRequestFailedException.ErrorCode == Constants.Blob.AlreadyExists)
                {
                    return(default);
Ejemplo n.º 2
0
 /// <summary>
 /// The <see cref="CreateIfNotExists"/> operation creates a new 0-length
 /// append blob.  If the append blob already exists, the content of
 /// the existing append blob will remain unchanged.  To add content to the append
 /// blob, call the <see cref="AppendBlockAsync"/> operation.
 ///
 /// For more information, see <see href="https://docs.microsoft.com/rest/api/storageservices/put-blob" />.
 /// </summary>
 /// <param name="httpHeaders">
 /// Optional standard HTTP header properties that can be set for the
 /// new append blob.
 /// </param>
 /// <param name="metadata">
 /// Optional custom metadata to set for this append blob.
 /// </param>
 /// <param name="cancellationToken">
 /// Optional <see cref="CancellationToken"/> to propagate
 /// notifications that the operation should be cancelled.
 /// </param>
 /// <returns>
 /// A <see cref="Response{BlobContentInfo}"/> describing the
 /// newly created append blob.
 /// </returns>
 /// <remarks>
 /// A <see cref="StorageRequestFailedException"/> will be thrown if
 /// a failure occurs.
 /// </remarks>
 public virtual Response <BlobContentInfo> CreateIfNotExists(
     BlobHttpHeaders?httpHeaders         = default,
     Metadata metadata                   = default,
     CancellationToken cancellationToken = default) =>
 CreateIfNotExistsInternal(
     httpHeaders,
     metadata,
     false,     // async
     cancellationToken)
 .EnsureCompleted();
Ejemplo n.º 3
0
 /// <summary>
 /// The <see cref="CreateIfNotExistsAsync"/> operation creates a new 0-length
 /// append blob.  If the append blob already exists, the content of
 /// the existing append blob will remain unchanged.  To add content to the append
 /// blob, call the <see cref="AppendBlockAsync"/> operation.
 ///
 /// For more information, see <see href="https://docs.microsoft.com/rest/api/storageservices/put-blob" />.
 /// </summary>
 /// <param name="httpHeaders">
 /// Optional standard HTTP header properties that can be set for the
 /// new append blob.
 /// </param>
 /// <param name="metadata">
 /// Optional custom metadata to set for this append blob.
 /// </param>
 /// <param name="cancellationToken">
 /// Optional <see cref="CancellationToken"/> to propagate
 /// notifications that the operation should be cancelled.
 /// </param>
 /// <returns>
 /// A <see cref="Response{BlobContentInfo}"/> describing the
 /// newly created append blob.
 /// </returns>
 /// <remarks>
 /// A <see cref="StorageRequestFailedException"/> will be thrown if
 /// a failure occurs.
 /// </remarks>
 public virtual async Task <Response <BlobContentInfo> > CreateIfNotExistsAsync(
     BlobHttpHeaders?httpHeaders         = default,
     Metadata metadata                   = default,
     CancellationToken cancellationToken = default) =>
 await CreateIfNotExistsInternal(
     httpHeaders,
     metadata,
     true,     // async
     cancellationToken)
 .ConfigureAwait(false);
Ejemplo n.º 4
0
 /// <summary>
 /// The <see cref="Create"/> operation creates a new 0-length
 /// append blob.  The content of any existing blob is overwritten with
 /// the newly initialized append blob.  To add content to the append
 /// blob, call the <see cref="AppendBlock"/> operation.
 ///
 /// For more information, see <see href="https://docs.microsoft.com/rest/api/storageservices/put-blob" />.
 /// </summary>
 /// <param name="httpHeaders">
 /// Optional standard HTTP header properties that can be set for the
 /// new append blob.
 /// </param>
 /// <param name="metadata">
 /// Optional custom metadata to set for this append blob.
 /// </param>
 /// <param name="accessConditions">
 /// Optional <see cref="AppendBlobAccessConditions"/> to add
 /// conditions on the creation of this new append blob.
 /// </param>
 /// <param name="cancellationToken">
 /// Optional <see cref="CancellationToken"/> to propagate
 /// notifications that the operation should be cancelled.
 /// </param>
 /// <returns>
 /// A <see cref="Response{BlobContentInfo}"/> describing the
 /// newly created append blob.
 /// </returns>
 /// <remarks>
 /// A <see cref="StorageRequestFailedException"/> will be thrown if
 /// a failure occurs.
 /// </remarks>
 public virtual Response <BlobContentInfo> Create(
     BlobHttpHeaders?httpHeaders = default,
     Metadata metadata           = default,
     AppendBlobAccessConditions?accessConditions = default,
     CancellationToken cancellationToken         = default) =>
 CreateInternal(
     httpHeaders,
     metadata,
     accessConditions,
     false,     // async
     cancellationToken)
 .EnsureCompleted();
Ejemplo n.º 5
0
 /// <summary>
 /// The <see cref="CreateAsync"/> operation creates a new 0-length
 /// append blob.  The content of any existing blob is overwritten with
 /// the newly initialized append blob.  To add content to the append
 /// blob, call the <see cref="AppendBlockAsync"/> operation.
 ///
 /// For more information, see <see href="https://docs.microsoft.com/rest/api/storageservices/put-blob" />.
 /// </summary>
 /// <param name="httpHeaders">
 /// Optional standard HTTP header properties that can be set for the
 /// new append blob.
 /// </param>
 /// <param name="metadata">
 /// Optional custom metadata to set for this append blob.
 /// </param>
 /// <param name="accessConditions">
 /// Optional <see cref="AppendBlobAccessConditions"/> to add
 /// conditions on the creation of this new append blob.
 /// </param>
 /// <param name="cancellationToken">
 /// Optional <see cref="CancellationToken"/> to propagate
 /// notifications that the operation should be cancelled.
 /// </param>
 /// <returns>
 /// A <see cref="Response{BlobContentInfo}"/> describing the
 /// newly created append blob.
 /// </returns>
 /// <remarks>
 /// A <see cref="StorageRequestFailedException"/> will be thrown if
 /// a failure occurs.
 /// </remarks>
 public virtual async Task <Response <BlobContentInfo> > CreateAsync(
     BlobHttpHeaders?httpHeaders = default,
     Metadata metadata           = default,
     AppendBlobAccessConditions?accessConditions = default,
     CancellationToken cancellationToken         = default) =>
 await CreateInternal(
     httpHeaders,
     metadata,
     accessConditions,
     true,     // async
     cancellationToken)
 .ConfigureAwait(false);
Ejemplo n.º 6
0
 public virtual async Task <Response <BlobContentInfo> > UploadAsync(
     Stream content,
     BlobHttpHeaders?blobHttpHeaders = default,
     Metadata metadata = default,
     BlobAccessConditions?blobAccessConditions   = default,
     IProgress <StorageProgress> progressHandler = default,
     CancellationToken cancellationToken         = default) =>
 await this.StagedUploadAsync(
     content,
     blobHttpHeaders,
     metadata,
     blobAccessConditions,
     progressHandler,
     async : true,
     cancellationToken : cancellationToken)
 .ConfigureAwait(false);
Ejemplo n.º 7
0
 public virtual Response <BlobContentInfo> Upload(
     Stream content,
     BlobHttpHeaders?blobHttpHeaders = default,
     Metadata metadata = default,
     BlobAccessConditions?blobAccessConditions   = default,
     IProgress <StorageProgress> progressHandler = default,
     CancellationToken cancellationToken         = default) =>
 this.StagedUploadAsync(
     content,
     blobHttpHeaders,
     metadata,
     blobAccessConditions,
     progressHandler,
     async: false,
     cancellationToken: cancellationToken)
 .EnsureCompleted();
Ejemplo n.º 8
0
 public virtual Task <Response <BlobContentInfo> > UploadAsync(
     FileInfo content,
     BlobHttpHeaders?blobHttpHeaders = default,
     Metadata metadata = default,
     BlobAccessConditions?blobAccessConditions       = default,
     IProgress <StorageProgress> progressHandler     = default,
     ParallelTransferOptions parallelTransferOptions = default,
     CancellationToken cancellationToken             = default) =>
 this.StagedUploadAsync(
     content,
     blobHttpHeaders,
     metadata,
     blobAccessConditions,
     progressHandler,
     parallelTransferOptions: parallelTransferOptions,
     async: true,
     cancellationToken: cancellationToken);
Ejemplo n.º 9
0
 /// <summary>
 /// The <see cref="CreateAsync"/> operation creates a new 0-length
 /// append blob.  The content of any existing blob is overwritten with
 /// the newly initialized append blob.  To add content to the append
 /// blob, call the <see cref="AppendBlockAsync"/> operation.
 ///
 /// For more information, see <see href="https://docs.microsoft.com/rest/api/storageservices/put-blob" />.
 /// </summary>
 /// <param name="httpHeaders">
 /// Optional standard HTTP header properties that can be set for the
 /// new append blob.
 /// </param>
 /// <param name="metadata">
 /// Optional custom metadata to set for this append blob.
 /// </param>
 /// <param name="accessConditions">
 /// Optional <see cref="AppendBlobAccessConditions"/> to add
 /// conditions on the creation of this new append blob.
 /// </param>
 /// <param name="cancellation">
 /// Optional <see cref="CancellationToken"/> to propagate
 /// notifications that the operation should be cancelled.
 /// </param>
 /// <returns>
 /// A <see cref="Task{Response{BlobContentInfo}}"/> describing the
 /// newly created append blob.
 /// </returns>
 /// <remarks>
 /// A <see cref="StorageRequestFailedException"/> will be thrown if
 /// a failure occurs.
 /// </remarks>
 public async Task <Response <BlobContentInfo> > CreateAsync(
     BlobHttpHeaders?httpHeaders = default,
     Metadata metadata           = default,
     AppendBlobAccessConditions?accessConditions = default,
     CancellationToken cancellation = default)
 {
     using (this.Pipeline.BeginLoggingScope(nameof(AppendBlobClient)))
     {
         this.Pipeline.LogMethodEnter(
             nameof(AppendBlobClient),
             message:
             $"{nameof(this.Uri)}: {this.Uri}\n" +
             $"{nameof(httpHeaders)}: {httpHeaders}\n" +
             $"{nameof(accessConditions)}: {accessConditions}");
         try
         {
             return(await BlobRestClient.AppendBlob.CreateAsync(
                        this.Pipeline,
                        this.Uri,
                        contentLength : default,
Ejemplo n.º 10
0
 public virtual Response <BlobContentInfo> Upload(
     FileInfo content,
     BlobHttpHeaders?blobHttpHeaders = default,
     Metadata metadata = default,
     BlobAccessConditions?blobAccessConditions       = default,
     CustomerProvidedKey?customerProvidedKey         = default,
     IProgress <StorageProgress> progressHandler     = default,
     ParallelTransferOptions parallelTransferOptions = default,
     CancellationToken cancellationToken             = default) =>
 this.StagedUploadAsync(
     content,
     blobHttpHeaders,
     metadata,
     blobAccessConditions,
     customerProvidedKey,
     progressHandler,
     parallelTransferOptions: parallelTransferOptions,
     async: false,
     cancellationToken: cancellationToken)
 .EnsureCompleted();
Ejemplo n.º 11
0
 public virtual Response <BlobContentInfo> Upload(
     FileInfo content,
     BlobHttpHeaders?httpHeaders                     = default,
     Metadata metadata                               = default,
     BlobAccessConditions?accessConditions           = default,
     IProgress <StorageProgress> progressHandler     = default,
     AccessTier?accessTier                           = default,
     ParallelTransferOptions parallelTransferOptions = default,
     CancellationToken cancellationToken             = default) =>
 StagedUploadAsync(
     content,
     httpHeaders,
     metadata,
     accessConditions,
     progressHandler,
     accessTier,
     parallelTransferOptions: parallelTransferOptions,
     async: false,
     cancellationToken: cancellationToken)
 .EnsureCompleted();
Ejemplo n.º 12
0
        /// <summary>
        /// The <see cref="CreateInternal"/> operation creates a new 0-length
        /// append blob.  The content of any existing blob is overwritten with
        /// the newly initialized append blob.  To add content to the append
        /// blob, call the <see cref="AppendBlockAsync"/> operation.
        ///
        /// For more information, see <see href="https://docs.microsoft.com/rest/api/storageservices/put-blob" />.
        /// </summary>
        /// <param name="httpHeaders">
        /// Optional standard HTTP header properties that can be set for the
        /// new append blob.
        /// </param>
        /// <param name="metadata">
        /// Optional custom metadata to set for this append blob.
        /// </param>
        /// <param name="accessConditions">
        /// Optional <see cref="AppendBlobAccessConditions"/> to add
        /// conditions on the creation of this new append blob.
        /// </param>
        /// <param name="async">
        /// Whether to invoke the operation asynchronously.
        /// </param>
        /// <param name="cancellationToken">
        /// Optional <see cref="CancellationToken"/> to propagate
        /// notifications that the operation should be cancelled.
        /// </param>
        /// <returns>
        /// A <see cref="Response{BlobContentInfo}"/> describing the
        /// newly created append blob.
        /// </returns>
        /// <remarks>
        /// A <see cref="StorageRequestFailedException"/> will be thrown if
        /// a failure occurs.
        /// </remarks>
        private async Task <Response <BlobContentInfo> > CreateInternal(
            BlobHttpHeaders?httpHeaders,
            Metadata metadata,
            AppendBlobAccessConditions?accessConditions,
            bool async,
            CancellationToken cancellationToken)
        {
            using (Pipeline.BeginLoggingScope(nameof(AppendBlobClient)))
            {
                Pipeline.LogMethodEnter(
                    nameof(AppendBlobClient),
                    message:
                    $"{nameof(Uri)}: {Uri}\n" +
                    $"{nameof(httpHeaders)}: {httpHeaders}\n" +
                    $"{nameof(accessConditions)}: {accessConditions}");
                try
                {
                    BlobErrors.VerifyHttpsCustomerProvidedKey(Uri, CustomerProvidedKey);

                    return(await BlobRestClient.AppendBlob.CreateAsync(
                               Pipeline,
                               Uri,
                               contentLength : default,
Ejemplo n.º 13
0
        /// <summary>
        /// Creates a new PageBlobClient object identical to the source but with the specified version ID.
        /// Pass "" to remove the version ID returning a URL to the base blob.
        /// </summary>
        /// <param name="versionId">version ID</param>
        /// <returns></returns>
        //public new PageBlobClient WithVersionId(string versionId) => (PageBlobUri)this.WithVersionIdImpl(versionId);

        //protected sealed override BlobClient WithVersionIdImpl(string versionId)
        //{
        //    var builder = new BlobUriBuilder(this.Uri) { VersionId = versionId };
        //    return new PageBlobClient(builder.ToUri(), this.Pipeline);
        //}

        /// <summary>
        /// The <see cref="CreateAsync"/> operation creates a new page blob of
        /// the specified <paramref name="size"/>.  The content of any
        /// existing blob is overwritten with the newly initialized page blob
        /// To add content to the page blob, call the
        /// <see cref="UploadPagesAsync"/> operation.
        ///
        /// For more information, see https://docs.microsoft.com/rest/api/storageservices/put-blob.
        /// </summary>
        /// <param name="size">
        /// Specifies the maximum size for the page blob, up to 8 TB.  The
        /// size must be aligned to a 512-byte boundary.
        /// </param>
        /// <param name="sequenceNumber">
        /// Optional user-controlled value that you can use to track requests.
        /// The value of the <paramref name="sequenceNumber"/> must be between
        /// 0 and 2^63 - 1.  The default value is 0.
        /// </param>
        /// <param name="httpHeaders">
        /// Optional standard HTTP header properties that can be set for the
        /// new page blob.
        /// </param>
        /// <param name="metadata">
        /// Optional custom metadata to set for this page blob.
        /// </param>
        /// <param name="accessConditions">
        /// Optional <see cref="PageBlobAccessConditions"/> to add
        /// conditions on the creation of this new page blob.
        /// </param>
        /// <param name="cancellation">
        /// Optional <see cref="CancellationToken"/> to propagate
        /// notifications that the operation should be cancelled.
        /// </param>
        /// <returns>
        /// A <see cref="Task{Response{BlobContentInfo}}"/> describing the
        /// newly created page blob.
        /// </returns>
        /// <remarks>
        /// A <see cref="StorageRequestFailedException"/> will be thrown if
        /// a failure occurs.
        /// </remarks>
        public async Task <Response <BlobContentInfo> > CreateAsync(
            long size,
            long?sequenceNumber         = default,
            BlobHttpHeaders?httpHeaders = default,
            Metadata metadata           = default,
            PageBlobAccessConditions?accessConditions = default,
            CancellationToken cancellation            = default)
        {
            using (this.Pipeline.BeginLoggingScope(nameof(PageBlobClient)))
            {
                this.Pipeline.LogMethodEnter(
                    nameof(PageBlobClient),
                    message:
                    $"{nameof(this.Uri)}: {this.Uri}\n" +
                    $"{nameof(size)}: {size}\n" +
                    $"{nameof(sequenceNumber)}: {sequenceNumber}\n" +
                    $"{nameof(httpHeaders)}: {httpHeaders}");
                try
                {
                    return(await BlobRestClient.PageBlob.CreateAsync(
                               this.Pipeline,
                               this.Uri,
                               contentLength : default,
Ejemplo n.º 14
0
        /// <summary>
        /// The <see cref="StagedUploadAsync"/> operation will create a new
        /// block blob of arbitrary size by uploading it as indiviually staged
        /// blocks if it's larger than the
        /// <paramref name="singleBlockThreshold"/>.
        /// </summary>
        /// <param name="content">
        /// A <see cref="Stream"/> containing the content to upload.
        /// </param>
        /// <param name="blobHttpHeaders">
        /// Optional standard HTTP header properties that can be set for the
        /// block blob.
        /// </param>
        /// <param name="metadata">
        /// Optional custom metadata to set for this block blob.
        /// </param>
        /// <param name="blobAccessConditions">
        /// Optional <see cref="BlobAccessConditions"/> to add conditions on
        /// the creation of this new block blob.
        /// </param>
        /// <param name="progressHandler">
        /// Optional <see cref="IProgress{StorageProgress}"/> to provide
        /// progress updates about data transfers.
        /// </param>
        /// <param name="singleBlockThreshold">
        /// The maximum size stream that we'll upload as a single block.  The
        /// default value is 256MB.
        /// </param>
        /// <param name="blockSize">
        /// The size of individually staged blocks.  The default value is 4MB.
        /// </param>
        /// <param name="async">
        /// </param>
        /// <param name="cancellationToken">
        /// Optional <see cref="CancellationToken"/> to propagate
        /// notifications that the operation should be cancelled.
        /// </param>
        /// <returns>
        /// A <see cref="Response{BlobContentInfo}"/> describing the
        /// state of the updated block blob.
        /// </returns>
        /// <remarks>
        /// A <see cref="StorageRequestFailedException"/> will be thrown if
        /// a failure occurs.
        /// </remarks>
        internal async Task <Response <BlobContentInfo> > StagedUploadAsync(
            Stream content,
            BlobHttpHeaders?blobHttpHeaders,
            Metadata metadata,
            BlobAccessConditions?blobAccessConditions,
            IProgress <StorageProgress> progressHandler,
            long singleBlockThreshold = BlockBlobClient.BlockBlobMaxUploadBlobBytes,
            int blockSize             = Constants.DefaultBufferSize,
            bool async = true,
            CancellationToken cancellationToken = default)
        {
            Debug.Assert(singleBlockThreshold <= BlockBlobClient.BlockBlobMaxUploadBlobBytes);

            var client     = new BlockBlobClient(this.Uri, this.Pipeline);
            var blockList  = new List <string>();
            var uploadTask = ChunkedUploader.UploadAsync(
                UploadStreamAsync,
                StageBlockAsync,
                CommitBlockListAsync,
                content,
                singleBlockThreshold,
                blockSize,
                async,
                cancellationToken);

            return(async ?
                   await uploadTask.ConfigureAwait(false) :
                   uploadTask.EnsureCompleted());

            // Upload the entire stream
            Task <Response <BlobContentInfo> > UploadStreamAsync(
                Stream content,
                bool async,
                CancellationToken cancellation) =>
            client.UploadInternal(
                content,
                blobHttpHeaders,
                metadata,
                blobAccessConditions,
                progressHandler,
                async,
                cancellationToken);

            // Upload a single chunk of the stream
            Task <Response <BlockInfo> > StageBlockAsync(
                Stream chunk,
                int blockNumber,
                bool async,
                CancellationToken cancellation)
            {
                // Create a new block ID
                var blockId = Constants.BlockNameFormat;

                blockId = String.Format(CultureInfo.InvariantCulture, blockId, blockNumber);
                blockId = Convert.ToBase64String(Encoding.UTF8.GetBytes(blockId));
                blockList.Add(blockId);

                // Upload the block
                return(client.StageBlockInternal(
                           blockId,
                           chunk,
                           null,
                           blobAccessConditions?.LeaseAccessConditions,
                           progressHandler,
                           async,
                           cancellationToken));
            }

            // Commit a series of chunks
            Task <Response <BlobContentInfo> > CommitBlockListAsync(
                bool async,
                CancellationToken cancellation) =>
            client.CommitBlockListInternal(
                blockList,
                blobHttpHeaders,
                metadata,
                blobAccessConditions,
                async,
                cancellationToken);
        }
Ejemplo n.º 15
0
        /// <summary>
        /// This operation will create a new
        /// block blob of arbitrary size by uploading it as indiviually staged
        /// blocks if it's larger than the
        /// <paramref name="singleBlockThreshold"/>.
        /// </summary>
        /// <param name="file">
        /// A <see cref="FileInfo"/> of the file to upload.
        /// </param>
        /// <param name="blobHttpHeaders">
        /// Optional standard HTTP header properties that can be set for the
        /// block blob.
        /// </param>
        /// <param name="metadata">
        /// Optional custom metadata to set for this block blob.
        /// </param>
        /// <param name="blobAccessConditions">
        /// Optional <see cref="BlobAccessConditions"/> to add conditions on
        /// the creation of this new block blob.
        /// </param>
        /// <param name="progressHandler">
        /// Optional <see cref="IProgress{StorageProgress}"/> to provide
        /// progress updates about data transfers.
        /// </param>
        /// <param name="singleBlockThreshold">
        /// The maximum size stream that we'll upload as a single block.  The
        /// default value is 256MB.
        /// </param>
        /// <param name="parallelTransferOptions">
        /// Optional <see cref="ParallelTransferOptions"/> to configure
        /// parallel transfer behavior.
        /// </param>
        /// <param name="async">
        /// </param>
        /// <param name="cancellationToken">
        /// Optional <see cref="CancellationToken"/> to propagate
        /// notifications that the operation should be cancelled.
        /// </param>
        /// <returns>
        /// A <see cref="Response{BlobContentInfo}"/> describing the
        /// state of the updated block blob.
        /// </returns>
        /// <remarks>
        /// A <see cref="StorageRequestFailedException"/> will be thrown if
        /// a failure occurs.
        /// </remarks>
        internal async Task <Response <BlobContentInfo> > StagedUploadAsync(
            FileInfo file,
            BlobHttpHeaders?blobHttpHeaders,
            Metadata metadata,
            BlobAccessConditions?blobAccessConditions,
            IProgress <StorageProgress> progressHandler,
            long singleBlockThreshold = BlockBlobClient.BlockBlobMaxUploadBlobBytes,
            ParallelTransferOptions parallelTransferOptions = default,
            bool async = true,
            CancellationToken cancellationToken = default)
        {
            Debug.Assert(singleBlockThreshold <= BlockBlobClient.BlockBlobMaxUploadBlobBytes);

            var client     = new BlockBlobClient(this.Uri, this.Pipeline);
            var blockMap   = new ConcurrentDictionary <long, string>();
            var blockName  = 0;
            var uploadTask = PartitionedUploader.UploadAsync(
                UploadStreamAsync,
                StageBlockAsync,
                CommitBlockListAsync,
                threshold => file.Length < threshold,
                memoryPool => new StreamPartitioner(file, memoryPool),
                singleBlockThreshold,
                parallelTransferOptions,
                async,
                cancellationToken);

            return(async ?
                   await uploadTask.ConfigureAwait(false) :
                   uploadTask.EnsureCompleted());

            string GetNewBase64BlockId(long blockOrdinal)
            {
                // Create and record a new block ID, storing the order information
                // (nominally the block's start position in the original stream)

                var newBlockName = Interlocked.Increment(ref blockName);
                var blockId      = Constants.BlockNameFormat;

                blockId = String.Format(CultureInfo.InvariantCulture, blockId, newBlockName);
                blockId = Convert.ToBase64String(Encoding.UTF8.GetBytes(blockId));
                var success = blockMap.TryAdd(blockOrdinal, blockId);

                Debug.Assert(success);

                return(blockId);
            }

            // Upload the entire stream
            async Task <Response <BlobContentInfo> > UploadStreamAsync()
            {
                using (var stream = file.OpenRead())
                {
                    return
                        (await client.UploadInternal(
                             stream,
                             blobHttpHeaders,
                             metadata,
                             blobAccessConditions,
                             progressHandler,
                             async,
                             cancellationToken)
                         .ConfigureAwait(false));
                }
            }

            // Upload a single partition of the stream
            Task <Response <BlockInfo> > StageBlockAsync(
                Stream partition,
                long blockOrdinal,
                bool async,
                CancellationToken cancellation)
            {
                var base64BlockId = GetNewBase64BlockId(blockOrdinal);

                //var bytes = new byte[10];
                //partition.Read(bytes, 0, 10);
                partition.Position = 0;
                //Console.WriteLine($"Commiting partition {blockOrdinal} => {base64BlockId}, {String.Join(" ", bytes)}");

                // Upload the block
                return(client.StageBlockInternal(
                           base64BlockId,
                           partition,
                           null,
                           blobAccessConditions?.LeaseAccessConditions,
                           progressHandler,
                           async,
                           cancellationToken));
            }

            // Commit a series of partitions
            Task <Response <BlobContentInfo> > CommitBlockListAsync(
                bool async,
                CancellationToken cancellation)
            {
                var base64BlockIds = blockMap.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value).ToArray();

                //Console.WriteLine($"Commiting block list:\n{String.Join("\n", base64BlockIds)}");

                return
                    (client.CommitBlockListInternal(
                         base64BlockIds,
                         blobHttpHeaders,
                         metadata,
                         blobAccessConditions,
                         async,
                         cancellationToken));
            }
        }
Ejemplo n.º 16
0
        /// <summary>
        /// Creates a new BlockBlobURL object identical to the source but with the specified version ID.
        /// </summary>
        /// <remarks>
        /// Pass null or empty string to remove the snapshot returning a URL to the base blob.
        /// </remarks>
        /// <param name="versionId">A string of the version identifier.</param>
        /// <returns></returns>
        //public new BlockBlobClient WithVersionId(string versionId) => (BlockBlobUri)this.WithVersionIdImpl(versionId);

        //protected sealed override Blobclient WithVersionIdImpl(string versionId)
        //{
        //    var builder = new BlobUriBuilder(this.Uri) { VersionId = versionId };
        //    return new BlockBlobClient(builder.ToUri(), this.Pipeline);
        //}

        /// <summary>
        /// The <see cref="UploadAsync"/> operation creates a new block  blob,
        /// or updates the content of an existing block blob.  Updating an
        /// existing block blob overwrites any existing metadata on the blob.
        ///
        /// Partial updates are not supported with <see cref="UploadAsync"/>;
        /// the content of the existing blob is overwritten with the content
        /// of the new blob.  To perform a partial update of the content of a
        /// block blob, use the <see cref="StageBlockAsync"/> and
        /// <see cref="CommitBlockListAsync" /> operations.
        ///
        /// For more information, see <see href="https://docs.microsoft.com/rest/api/storageservices/put-blob" />.
        /// </summary>
        /// <param name="content">
        /// A <see cref="Stream"/> containing the content to upload.
        /// </param>
        /// <param name="blobHttpHeaders">
        /// Optional standard HTTP header properties that can be set for the
        /// block blob.
        /// </param>
        /// <param name="metadata">
        /// Optional custom metadata to set for this block blob.
        /// </param>
        /// <param name="blobAccessConditions">
        /// Optional <see cref="BlockBlobClient"/> to add
        /// conditions on the creation of this new block blob.
        /// </param>
        /// <param name="progressHandler">
        /// Optional <see cref="IProgress{StorageProgress}"/> to provide
        /// progress updates about data transfers.
        /// </param>
        /// <param name="cancellation">
        /// Optional <see cref="CancellationToken"/> to propagate
        /// notifications that the operation should be cancelled.
        /// </param>
        /// <returns>
        /// A <see cref="Task{Response{BlobContentInfo}}"/> describing the
        /// state of the updated block blob.
        /// </returns>
        /// <remarks>
        /// A <see cref="StorageRequestFailedException"/> will be thrown if
        /// a failure occurs.
        /// </remarks>
        public StorageTask <Response <BlobContentInfo> > UploadAsync(
            Stream content,
            BlobHttpHeaders?blobHttpHeaders = default,
            Metadata metadata = default,
            BlobAccessConditions?blobAccessConditions   = default,
            IProgress <StorageProgress> progressHandler = default,
            CancellationToken cancellation = default)
        {
            content = content.WithNoDispose().WithProgress(progressHandler);
            var uploadAttempt = 0;

            return(StorageTask.Create(
                       pipeline: this.Pipeline,
                       cancellationToken: cancellation,
                       reliabilityConfiguration: new ReliabilityConfiguration(reset: () => content.Seek(0, SeekOrigin.Begin)),
                       operation:
                       async(p, ct) =>
            {
                using (p.BeginLoggingScope(nameof(BlockBlobClient)))
                {
                    p.LogMethodEnter(
                        nameof(BlockBlobClient),
                        message:
                        $"{nameof(this.Uri)}: {this.Uri}\n" +
                        $"{nameof(blobHttpHeaders)}: {blobHttpHeaders}\n" +
                        $"{nameof(blobAccessConditions)}: {blobAccessConditions}");
                    try
                    {
                        p.LogTrace($"Upload attempt {++uploadAttempt}");
                        return await BlobRestClient.BlockBlob.UploadAsync(
                            p,
                            this.Uri,
                            body: content,
                            contentLength: content.Length,
                            blobContentType: blobHttpHeaders?.ContentType,
                            blobContentEncoding: blobHttpHeaders?.ContentEncoding,
                            blobContentLanguage: blobHttpHeaders?.ContentLanguage,
                            blobContentHash: blobHttpHeaders?.ContentHash,
                            blobCacheControl: blobHttpHeaders?.CacheControl,
                            metadata: metadata,
                            leaseId: blobAccessConditions?.LeaseAccessConditions?.LeaseId,
                            blobContentDisposition: blobHttpHeaders?.ContentDisposition,
                            ifModifiedSince: blobAccessConditions?.HttpAccessConditions?.IfModifiedSince,
                            ifUnmodifiedSince: blobAccessConditions?.HttpAccessConditions?.IfUnmodifiedSince,
                            ifMatch: blobAccessConditions?.HttpAccessConditions?.IfMatch,
                            ifNoneMatch: blobAccessConditions?.HttpAccessConditions?.IfNoneMatch,
                            cancellation: ct)
                        .ConfigureAwait(false);
                    }
                    catch (Exception ex)
                    {
                        p.LogException(ex);
                        throw;
                    }
                    finally
                    {
                        p.LogMethodExit(nameof(BlockBlobClient));
                    }
                }
            }));
        }