private async Task <MobileAppContentFile> RenewStorageUri(IMobileAppContentFileRequestBuilder contentFileRequestBuilder) { logger.LogInformation($"Renewing SAS URI for {contentFileRequestBuilder.RequestUrl}."); await contentFileRequestBuilder.RenewUpload().Request().PostAsync(); return(await WaitForStateAsync(contentFileRequestBuilder, MobileAppContentFileUploadState.AzureStorageUriRenewalSuccess)); }
// waits for the desired status, refreshing the file along the way private async Task <MobileAppContentFile> WaitForStateAsync(IMobileAppContentFileRequestBuilder contentFileRequestBuilder, MobileAppContentFileUploadState state) { logger.LogInformation($"Waiting for app content file to have a state of {state}."); var waitStopwatch = Stopwatch.StartNew(); while (true) { var contentFile = await contentFileRequestBuilder.Request().GetAsync(); if (contentFile.UploadState == state) { logger.LogInformation($"Waited {waitStopwatch.ElapsedMilliseconds}ms for app content file to have a state of {state}."); return(contentFile); } var failedStates = new[] { MobileAppContentFileUploadState.AzureStorageUriRequestFailed, MobileAppContentFileUploadState.AzureStorageUriRenewalFailed, MobileAppContentFileUploadState.CommitFileFailed }; if (failedStates.Contains(contentFile.UploadState.GetValueOrDefault())) { throw new InvalidOperationException($"{nameof(contentFile.UploadState)} is in a failed state of {contentFile.UploadState} - was waiting for {state}."); } const int waitTimeout = 600000; const int testInterval = 2000; if (waitStopwatch.ElapsedMilliseconds > waitTimeout) { throw new InvalidOperationException($"Timed out waiting for {nameof(contentFile.UploadState)} of {state} - current state is {contentFile.UploadState}."); } await Task.Delay(testInterval); } }
private async Task CreateBlobAsync(IntuneAppPackage package, MobileAppContentFile contentFile, IMobileAppContentFileRequestBuilder contentFileRequestBuilder) { var blockCount = 0; var blockIds = new List <string>(); const int chunkSize = 25 * 1024 * 1024; package.Data.Seek(0, SeekOrigin.Begin); var lastBlockId = (Math.Ceiling((double)package.Data.Length / chunkSize) - 1).ToString("0000"); var sw = Stopwatch.StartNew(); foreach (var chunk in Chunk(package.Data, chunkSize, false)) { if (sw.ElapsedMilliseconds >= 450000) { contentFile = await RenewStorageUri(contentFileRequestBuilder); sw.Restart(); } var blockId = blockCount++.ToString("0000"); logger.LogInformation($"Uploading block {blockId} of {lastBlockId} to {contentFile.AzureStorageUri}."); await using (var ms = new MemoryStream(chunk)) { try { await TryPutBlockAsync(contentFile, blockId, ms); } catch (StorageException ex) when(ex.RequestInformation.HttpStatusCode == 403) { // normally the timer should account for renewing upload URIs, but the Intune APIs are fundamentally unstable and sometimes 403s will be encountered randomly contentFile = await RenewStorageUri(contentFileRequestBuilder); sw.Restart(); await TryPutBlockAsync(contentFile, blockId, ms); } } blockIds.Add(blockId); } await new CloudBlockBlob(new Uri(contentFile.AzureStorageUri)).PutBlockListAsync(blockIds); }