Download container blobs event if you don’t know the blob names in a container.
// Authorization
StorageCredentials credential = new StorageCredentials(AccountName, AccountKey);
_account = new CloudStorageAccount(credential, false);
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Auth;
using Microsoft.WindowsAzure.Storage.Blob;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
namespace AzureBlobDownloaderAndRename
{
public class AzureBlobStorageFileDownloader
{
private readonly string _pathOfStorage;
private readonly CloudStorageAccount _account;
private readonly List<ExcelSheetSchema> _filesToDownload;
private string errors;
private string nameChangesFile = "D:\\FileNamesChanged.txt";
private static int Counter = 0; // Will restrict the number of async tasks.
private string ErrorsFileLog = "D:\\ErrorsFilesLog.txt";
public AzureBlobStorageFileDownloader(string pathOfStorage,
List<ExcelSheetSchema> filesToDownload,
string AccountName,
string AccountKey)
{
_filesToDownload = filesToDownload;
_pathOfStorage = pathOfStorage;
if (!string.IsNullOrWhiteSpace(AccountName)
&& !string.IsNullOrWhiteSpace(AccountKey))
{
StorageCredentials credential = new StorageCredentials(AccountName, AccountKey);
_account = new CloudStorageAccount(credential, false);
}
}
/// <summary>
/// Download the files from Azure container.
/// It will only download the first file from container blob list.
/// </summary>
public void GetFiles()
{
if (_account == null)
{
Console.WriteLine("\nAccount is null\n");
return;
}
if (string.IsNullOrEmpty(_pathOfStorage) || !Directory.Exists(_pathOfStorage))
{
Console.WriteLine("\nFile storage path {0} is invalid\n", _pathOfStorage);
return;
}
var blobClient = _account.CreateCloudBlobClient(); // Create blobClient
int count = 1;
_filesToDownload.ForEach(async file =>
{
try
{
var container = blobClient.GetContainerReference(file.ContainerName); // get reference to container
var continuationToken = new Microsoft.WindowsAzure.Storage.Blob.BlobContinuationToken(); // create a continuation token need for async blob list
var blobs = container.ListBlobsSegmentedAsync(continuationToken).Result; // get list of blobs
if (blobs.Results.Count() == 0) throw new Exception();
var blobFound = blobs.Results.FirstOrDefault(); // as we have only one blob, so used FirstOrDefault
if (blobFound == null) throw new Exception();
var blobFileInfo = new FileInfo(blobFound.Uri.ToString()); // file
var blobName = blobFileInfo.Name; // Find the blob name from Uri
var blobDownloadable = container.GetBlobReference(blobName); // Get reference to blob
blobDownloadable.FetchAttributesAsync().Wait(); // Fetch the blob information.
Counter++;
if (Counter > 10)
{
// wait until Counter is not less Counter
while (Counter > 10)
{
Thread.Sleep(20000); // After every 20 Seconds, the thread will check if total count is less than 10 or not.
}
}
Console.WriteLine("\nDownloading File {0}. {1}, Started At {2}, File Size {3} MB", count, blobName, DateTime.Now, blobDownloadable.Properties.Length / (1024 * 1024));
count++;
// Download blob to custom file name.
try
{
await DownloadFile(blobDownloadable, $"{file.Title}{blobFileInfo.Extension}");
Console.WriteLine("Downloaded {0}", blobDownloadable.Name);
Counter--;
}
catch (Exception)
{
// save the name of files which are not completed yet.
errors += $@"{blobName}\n";
}
}
catch (Exception)
{
errors += $@"container: {file.ContainerName}\n";
}
});
Console.WriteLine("Below files were not downloaded");
Console.WriteLine(errors);
File.AppendAllText(ErrorsFileLog, errors);
}
/// Downloads files from azure cloud.
/// Also checks if same file is there in the list(you can change it to check folder files by changing the function 'CheckIfFileIsRepeated').
/// If there is, then append a number.
private async Task DownloadFile(CloudBlob cloudBlob, string fileName)
{
var newFileName = fileName;
// check if file name contains invalid characters
if(fileName.IndexOfAny(Path.GetInvalidFileNameChars()) != -1)
{
// create new file name by removing the invalid chars
newFileName = Regex.Replace(fileName, "[\\/:\" *?<>|]+", "_");
// create new name and save to text file.
File.AppendAllText(nameChangesFile, $"{fileName} is renamed to {newFileName}");
}
// checks whether file title is repeated
if (CheckIfFileIsRepeated(fileName))
{
int num = 1;
// rename only if the file is not previously downloaded
// loop till we don't get unique name
var tempNewFileName = newFileName;
tempNewFileName = Path.GetFileNameWithoutExtension(tempNewFileName) + "_" + num + new FileInfo(fileName).Extension;
while (File.Exists($"{_pathOfStorage}/{tempNewFileName}"))
{
num++;
tempNewFileName = Path.GetFileNameWithoutExtension(tempNewFileName) + "_" + num + new FileInfo(fileName).Extension;
}
// now put the number at the end
newFileName = Path.GetFileNameWithoutExtension(newFileName) + "_" + num + new FileInfo(fileName).Extension;
}
if (!File.Exists($"{_pathOfStorage}/{newFileName}"))
await cloudBlob.DownloadToFileAsync($"{_pathOfStorage}/{newFileName}", FileMode.Create);
}
/// this checks if our list contains filenames which are repeated
private bool CheckIfFileIsRepeated(string filename)
{
if (_filesToDownload.Where(ll => String.Compare(ll.Title, Path.GetFileNameWithoutExtension(filename), true) == 0).Count() > 1)
{
return true;
}
else
{
return false;
}
}
/// checks if file is already appended with x number
private bool CheckIfFileIsAppenededWithXNumber(int number, string fileNameWithOutExtension)
{
var splitedString = fileNameWithOutExtension.Split('_');
if (splitedString.Length == 0) return false;
try
{
var ch = Convert.ToInt32(splitedString[splitedString.Length - 1]);
if (ch == number) return true;
return false;
}
catch (Exception)
{
return false;
}
}
}
}
This is for reference only. There may be few changes needed as SDK changes. I have used nuget package : unofficial.windowsazure.mediaservices.extensions and targeted .Net core 2.2