private IEnumerable<TransferEntry> EnumerateLocationRecursive(CancellationToken cancellationToken) { string fullPrefix = Uri.UnescapeDataString(this.location.FileDirectory.Uri.AbsolutePath); // Normalize full prefix to end with slash. if (!string.IsNullOrEmpty(fullPrefix) && !fullPrefix.EndsWith("/", StringComparison.OrdinalIgnoreCase)) { fullPrefix += '/'; } Stack<CloudFileDirectory> directoriesToList = new Stack<CloudFileDirectory>(); directoriesToList.Push(this.location.FileDirectory); string[] pathSegList = null; bool passedContinuationToken = false; int pathSegListIndex = 0; if (null != this.listContinuationToken) { pathSegList = this.listContinuationToken.FilePath.Split(new char[] { UriDelimiter }); } else { passedContinuationToken = true; } while (0 != directoriesToList.Count) { CloudFileDirectory directory = directoriesToList.Pop(); string dirAbsolutePath = Uri.UnescapeDataString(directory.Uri.AbsolutePath); if (dirAbsolutePath[dirAbsolutePath.Length - 1] != UriDelimiter) { dirAbsolutePath = dirAbsolutePath + UriDelimiter; } Stack<CloudFileDirectory> innerDirList = new Stack<CloudFileDirectory>(); FileContinuationToken continuationToken = null; // To check whether reached continuation token by dir or file in this round. bool checkFile = false; bool passedSubFolder = false; string continuationTokenSeg = null; if (!passedContinuationToken) { if (pathSegList.Length - 1 == pathSegListIndex) { checkFile = true; } continuationTokenSeg = pathSegList[pathSegListIndex]; pathSegListIndex++; } do { FileResultSegment resultSegment = null; Utils.CheckCancellation(cancellationToken); ErrorEntry errorEntry = null; try { FileRequestOptions requestOptions = Transfer_RequestOptions.DefaultFileRequestOptions; resultSegment = directory.ListFilesAndDirectoriesSegmented( ListFilesSegmentSize, continuationToken, requestOptions, null); } catch (Exception ex) { string errorMessage = string.Format( CultureInfo.CurrentCulture, Resources.FailedToEnumerateDirectory, this.location.FileDirectory.Uri.AbsoluteUri, string.Empty); TransferException exception = new TransferException(TransferErrorCode.FailToEnumerateDirectory, errorMessage, ex); errorEntry = new ErrorEntry(exception); } if (null != errorEntry) { yield return errorEntry; yield break; } continuationToken = resultSegment.ContinuationToken; foreach (IListFileItem fileItem in resultSegment.Results) { Utils.CheckCancellation(cancellationToken); if (fileItem is CloudFileDirectory) { if (checkFile || passedContinuationToken || passedSubFolder) { innerDirList.Push(fileItem as CloudFileDirectory); } else { CloudFileDirectory cloudDir = fileItem as CloudFileDirectory; string fullPath = Uri.UnescapeDataString(cloudDir.Uri.AbsolutePath); string segName = fullPath.Remove(0, dirAbsolutePath.Length); int compareResult = string.Compare(segName, continuationTokenSeg, StringComparison.OrdinalIgnoreCase); if (compareResult >= 0) { passedSubFolder = true; innerDirList.Push(cloudDir); if (compareResult > 0) { passedContinuationToken = true; } } } } else if (fileItem is CloudFile) { if (!checkFile && !passedContinuationToken) { continue; } CloudFile cloudFile = fileItem as CloudFile; string fullPath = Uri.UnescapeDataString(cloudFile.Uri.AbsolutePath); string relativePath = fullPath.Remove(0, fullPrefix.Length); if (passedContinuationToken) { yield return new AzureFileEntry( relativePath, cloudFile, new AzureFileListContinuationToken(relativePath)); } else { string segName = fullPath.Remove(0, dirAbsolutePath.Length); int compareResult = string.Compare(segName, continuationTokenSeg, StringComparison.OrdinalIgnoreCase); if (compareResult < 0) { continue; } passedContinuationToken = true; if (compareResult > 0) { yield return new AzureFileEntry( relativePath, cloudFile, new AzureFileListContinuationToken(relativePath)); } } } } } while (continuationToken != null); if (checkFile) { passedContinuationToken = true; } if (innerDirList.Count <= 0) { if (!checkFile && !passedContinuationToken) { passedContinuationToken = true; } } else { while (innerDirList.Count > 0) { directoriesToList.Push(innerDirList.Pop()); } } } }
/// <summary> /// Enumerates the blobs present in the storage location referenced by this object. /// </summary> /// <param name="cancellationToken">CancellationToken to cancel the method.</param> /// <returns>Enumerable list of TransferEntry objects found in the storage location referenced by this object.</returns> public IEnumerable<TransferEntry> EnumerateLocation(CancellationToken cancellationToken) { Utils.CheckCancellation(cancellationToken); string filePattern = this.SearchPattern ?? string.Empty; // Exceed-limit-length patterns surely match no files. int maxFileNameLength = this.GetMaxFileNameLength(); if (filePattern.Length > maxFileNameLength) { yield break; } CloudBlobContainer container = this.location.BlobDirectory.Container; BlobRequestOptions requestOptions = Transfer_RequestOptions.DefaultBlobRequestOptions; BlobContinuationToken continuationToken = (this.listContinuationToken == null ? null : this.listContinuationToken.BlobContinuationToken); bool passedContinuationToken = (this.listContinuationToken == null); string dirPrefix = this.location.BlobDirectory.Prefix; string patternPrefix = dirPrefix + filePattern; do { BlobResultSegment resultSegment = null; ErrorEntry errorEntry = null; Utils.CheckCancellation(cancellationToken); try { // TODO: Currently keep it to be a sync call here. We may need to change this to be async and cancellable in the future. resultSegment = container.ListBlobsSegmented( patternPrefix, true, BlobListingDetails.Snapshots, ListBlobsSegmentSize, continuationToken, requestOptions, null); } catch (Exception ex) { string errorMessage = string.Format( CultureInfo.CurrentCulture, Resources.FailedToEnumerateDirectory, this.location.BlobDirectory.Uri.AbsoluteUri, filePattern); TransferException exception = new TransferException(TransferErrorCode.FailToEnumerateDirectory, errorMessage, ex); errorEntry = new ErrorEntry(exception); } if (null != errorEntry) { // Just return an error entry if we cannot access the container yield return errorEntry; // TODO: What should we do if some entries have been listed successfully? yield break; } foreach (IListBlobItem blobItem in resultSegment.Results) { Utils.CheckCancellation(cancellationToken); CloudBlob blob = blobItem as CloudBlob; if (null != blob) { if (!this.IncludeSnapshots && blob.SnapshotTime.HasValue) { continue; } if (!passedContinuationToken) { int compareResult = string.Compare(this.listContinuationToken.BlobName, blob.Name, StringComparison.Ordinal); if (compareResult < 0) { passedContinuationToken = true; } else if (0 == compareResult) { if (IsSnapshotTimeEarlier(this.listContinuationToken.SnapshotTime, blob.SnapshotTime)) { passedContinuationToken = true; } } if (!passedContinuationToken) { continue; } } // TODO: currrently not support search for files with prefix specified without considering sub-directory. bool returnItOrNot = this.Recursive ? blob.Name.StartsWith(patternPrefix, StringComparison.Ordinal) : blob.Name.Equals(patternPrefix, StringComparison.Ordinal); if (returnItOrNot) { yield return new AzureBlobEntry( blob.Name.Remove(0, dirPrefix.Length), blob, new AzureBlobListContinuationToken(continuationToken, blob.Name, blob.SnapshotTime)); } } } continuationToken = resultSegment.ContinuationToken; } while (continuationToken != null); }
private IEnumerable<TransferEntry> EnumerateLocationNonRecursive(string fileName, CancellationToken cancellationToken) { Utils.CheckCancellation(cancellationToken); if (fileName == null || fileName.Length == 0 || fileName.Length > MaxDirectoryAndFileNameLength) { // Empty string or exceed-limit-length file name surely match no files. yield break; } if (this.listContinuationToken != null) { int compareResult = string.Compare(fileName, this.listContinuationToken.FilePath, StringComparison.Ordinal); if (compareResult <= 0) { yield break; } } CloudFile cloudFile = this.location.FileDirectory.GetFileReference(fileName); FileRequestOptions requestOptions = Transfer_RequestOptions.DefaultFileRequestOptions; ErrorEntry errorEntry = null; bool exist = false; try { exist = cloudFile.Exists(requestOptions); } catch (Exception ex) { string errorMessage = string.Format( CultureInfo.CurrentCulture, Resources.FailedToEnumerateDirectory, this.location.FileDirectory.Uri.AbsoluteUri, fileName); // Use TransferException to be more specific about the cloud file URI. TransferException exception = new TransferException(TransferErrorCode.FailToEnumerateDirectory, errorMessage, ex); errorEntry = new ErrorEntry(exception); } if (null != errorEntry) { yield return errorEntry; } else if (exist) { yield return new AzureFileEntry(fileName, cloudFile, new AzureFileListContinuationToken(fileName)); } }