Overwrite filename in SetAttributesCallback in Azure Storage Data Movement Library - azure-blob-storage

I need to lowercase all filenames during UploadDirectoryAsync - is this possible to control or set via 'SetAttributesCallback'??
I cannot control the local physical files or rename them locally before uploading them to azure via Azure Storage Data Movement Library.
The end result will be that source and destination filename always will be with lowercase.
Any solution out there??

I need to lowercase all filenames during UploadDirectoryAsync - is this possible to control or set via 'SetAttributesCallback'
Yes, we could do that in the SetAttributesCallback, currently there is no rename Azure blob API, so we could upload the required renamed file in the SetAttributesCallback and delete the UploadDirectoryAsync load file. I also test it on my side, it works correctly.
The following is my demo code.
using System;
using System.IO;
using System.Linq;
using System.Threading;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.WindowsAzure.Storage.DataMovement;
namespace DataMovementTest
{
class Program
{
static void Main(string[] args)
{
string storageConnectionString = "storage connection string";
CloudStorageAccount account = CloudStorageAccount.Parse(storageConnectionString);
CloudBlobClient blobClient = account.CreateCloudBlobClient();
CloudBlobContainer blobContainer = blobClient.GetContainerReference("container name");
blobContainer.CreateIfNotExists();
var destDir = blobContainer.GetDirectoryReference("directory name");
string sourceDirPath = #"local path";
var options = new UploadDirectoryOptions
{
Recursive = false,
BlobType = BlobType.BlockBlob
};
using (MemoryStream journalStream = new MemoryStream())
{
// Store the transfer context in a streamed journal.
DirectoryTransferContext context = new DirectoryTransferContext(journalStream)
{
SetAttributesCallback = (destination) =>
{
CloudBlob destBlob = destination as CloudBlob;
if (System.Text.RegularExpressions.Regex.IsMatch(destBlob.Uri.Segments.Last(), "[A-Z]")) //check whether blobName contains uppercase
{
var path = sourceDirPath + $"/{destBlob.Uri.Segments.Last()}";
Console.WriteLine(path);
var renameBlob = destDir.GetBlockBlobReference(destBlob.Uri.Segments.Last().ToLower());
using (var fileStream = File.OpenRead(path))
{
renameBlob.UploadFromStream(fileStream);
}
destBlob.DeleteIfExists();
}
},
ShouldTransferCallback = (source, destination) => true
};
CancellationTokenSource cancellationSource = new CancellationTokenSource();
try
{
// Start the upload
var uploadResult = TransferManager.UploadDirectoryAsync(sourceDirPath, destDir, options, context, cancellationSource.Token).Result;
}
catch (Exception e)
{
Console.WriteLine("The transfer is cancelled: {0}", e.Message);
}
Console.WriteLine("Files in directory {0} uploading to {1} is finished.", sourceDirPath, destDir.Uri.ToString());
}
}
}
}

Related

How to get the name of all the folders and subfolders of an Azure container

I have this container Ofertas with 3 folders
within each of the 3 main folders there are hundreds of subfolders and within each of these subfolders are the files or blobs
I need to get the list of these subfolders but I only know how to get the principal folder names
string connectionString = _configuration.GetConnectionString("ArchivadoHistoricoOfertas");
BlobServiceClient blobServiceClient = new BlobServiceClient(connectionString);
BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient("ofertas");
var blobFolders = containerClient.GetBlobsByHierarchy(BlobTraits.None, BlobStates.None, "/").Where(b => b.IsPrefix).ToList();
blobFolders.ForEach(f => {
Console.WriteLine("Folder Prefix: " + f.Prefix);
});
I have tried this
But here the code stops and nothing happens
Any idea, please?
Thanks
I tried in my environment and got below results:
If you need to get all folder, subfolder and files in structure you can use below code.
Code:
class Program
{
static async Task Main(string[] args)
{
// Get a connection string to our Azure Storage account.
string connectionString = "<connect strng>";
string containerName = "test";
Console.WriteLine($"Getting blobs and virtual directories for container '{containerName}'");
BlobContainerClient container = new BlobContainerClient(connectionString, containerName);
await ListBlobsForPrefixRecursive(container,"", 0);
}
public static async Task ListBlobsForPrefixRecursive(BlobContainerClient container, string prefix, int level)
{
string spaces = new string(' ', level);
Console.WriteLine($"{spaces}- {prefix}");
await foreach (Page<BlobHierarchyItem> page in container.GetBlobsByHierarchyAsync(prefix: prefix, delimiter: "/").AsPages())
{
foreach (var blob in page.Values.Where(item => item.IsBlob).Select(item => item.Blob))
{
Console.WriteLine($"{spaces} {blob.Name}");
}
var prefixes = page.Values.Where(item => item.IsPrefix).Select(item => item.Prefix);
foreach (var s in prefixes)
{
await ListBlobsForPrefixRecursive(container, s, level + 1);
}
}
}
}
Output:
If you need to get the particular root folder to get subfolder you use the below code:
Code:
using Azure.Storage.Blobs;
namespace storage326
{
class Program
{
static void Main(string[] args)
{
string connectionString = "< storage account>";
string rootfolder = "demo";
List<string> subfolder = new List<string>();
BlobServiceClient blobServiceClient = new BlobServiceClient(connectionString);
string containerName = "test";
BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(containerName);
var blobs = containerClient.GetBlobs(prefix: rootfolder);
foreach (var blob in blobs)
{
//Console.WriteLine(blob.Name);
string[] sub_names = blob.Name.Split('/');
if (sub_names.Length > 2 && !subfolder.Contains(sub_names[1]))
{
subfolder.Add(sub_names[1]);
}
}
foreach (var s in subfolder)
{
Console.WriteLine(rootfolder+"/"+ s);
}
}
}
}
Console:

C# :Move blobs from one container to another using Azure.Storage.Blobs client library

I am using the recent Azure.Storage.Blobs client library . I have seen many examples for copy and delete in CloudblockBlob Client using the StartCopyAsync() method but for the newer version I am not able to find anything .
I need to move files from one container to another in the same storage account .
This is the old version
CloudBlobClient blobClient = StorageAccount.CreateCloudBlobClient();
CloudBlobContainer sourceContainer = blobClient.GetContainerReference(SourceContainer);
CloudBlobContainer targetContainer = blobClient.GetContainerReference(TargetContainer);
CloudBlockBlob sourceBlob = sourceContainer.GetBlockBlobReference(fileToMove);
CloudBlockBlob targetBlob = targetContainer.GetBlockBlobReference(newFileName);
await targetBlob.StartCopyAsync(sourceBlob);
Please try the following code. It makes use of Azure.Storage.Blobs (12.9.1).
using System;
using System.Threading.Tasks;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
namespace SO68668289
{
class Program
{
private const string connectionString = "DefaultEndpointsProtocol=https;AccountName=account-name;AccountKey=account-key==";
private const string sourceContainer = "source";
private const string targetContainer = "target";
private const string blobName = "blob-name.txt";
static async Task Main(string[] args)
{
BlobServiceClient serviceClient = new BlobServiceClient(connectionString);
BlobContainerClient sourceContainerClient = serviceClient.GetBlobContainerClient(sourceContainer);
BlobContainerClient targetContainerClient = serviceClient.GetBlobContainerClient(targetContainer);
BlobClient sourceBlobClient = sourceContainerClient.GetBlobClient(blobName);
BlobClient targetBlobClient = targetContainerClient.GetBlobClient(blobName);
Console.WriteLine("Sending copy blob request....");
var result = await targetBlobClient.StartCopyFromUriAsync(sourceBlobClient.Uri);
Console.WriteLine("Copy blob request sent....");
Console.WriteLine("============");
bool isBlobCopiedSuccessfully = false;
do
{
Console.WriteLine("Checking copy status....");
var targetBlobProperties = await targetBlobClient.GetPropertiesAsync();
Console.WriteLine($"Current copy status = {targetBlobProperties.Value.CopyStatus}");
if (targetBlobProperties.Value.CopyStatus == CopyStatus.Pending)
{
System.Threading.Thread.Sleep(1000);
}
else
{
isBlobCopiedSuccessfully = targetBlobProperties.Value.CopyStatus == CopyStatus.Success;
break;
}
} while (true);
if (isBlobCopiedSuccessfully)
{
Console.WriteLine("Blob copied successfully. Now deleting source blob...");
await sourceBlobClient.DeleteAsync();
}
}
}
}

C# Azure.Storage.Blobs SDK How to list and zip all files in a container and store the zip in another container

I have around 100 files in a container . I want to call a function to zip all files and send it to another container . I am using the Azure.Storage.Blobs version 12.9.1
var blob = container.GetBlockBlobReference(outputFilename);
using (var stream = await blob.OpenWriteAsync())
using (var zip = new ZipArchive(stream, ZipArchiveMode.Create))
{
for (int i = 0; i < 2000; i++)
{
using (var randomStream = CreateRandomStream(2))
{
var entry = zip.CreateEntry($"{i}.zip", CompressionLevel.Optimal);
using (var innerFile = entry.Open())
{
await randomStream.CopyToAsync(innerFile);
}
}
}
}
This is for the older version . How can we achieve it using the new sdk
Can you please try with this code.
Solution 1: Try this code to generate Zip file. After generating zip file upload it to container
public void ZipFilesToResponse(HttpResponseBase response, IEnumerable<Asset> files, string zipFileName)
{
using (var zipOutputStream = new ZipOutputStream(response.OutputStream))
{
zipOutputStream.SetLevel(0); // 0 - store only to 9 - means best compression
response.BufferOutput = false;
response.AddHeader("Content-Disposition", "attachment; filename=" + zipFileName);
response.ContentType = "application/octet-stream";
foreach (var file in files)
{
var entry = new ZipEntry(file.FilenameSlug())
{
DateTime = DateTime.Now,
Size = file.Filesize
};
zipOutputStream.PutNextEntry(entry);
storageService.ReadToStream(file, zipOutputStream);
response.Flush();
if (!response.IsClientConnected)
{
break;
}
}
zipOutputStream.Finish();
zipOutputStream.Close();
}
response.End();
}
For more details refer this link
Solution 2:Also try with this code
using System;
using System.Threading.Tasks;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using System.IO;
using ICSharpCode.SharpZipLib.Zip;
namespace SO68566758
{
class Program
{
private const string connectionString = "DefaultEndpointsProtocol=https;AccountName=account-name;AccountKey=account-key";
private const string sourceContainer = "source-container";
private const string targetContainer = "target-container";
private const string outputBlobName = "backup.zip";
static async Task Main(string[] args)
{
BlobServiceClient serviceClient = new BlobServiceClient(connectionString);
BlobContainerClient sourceContainerClient = serviceClient.GetBlobContainerClient(sourceContainer);
BlobContainerClient targetContainerClient = serviceClient.GetBlobContainerClient(targetContainer);
var blobs = sourceContainerClient.GetBlobsAsync();
using (var fs = new FileStream(outputBlobName, FileMode.OpenOrCreate))
{
using (var zipOutputStream = new ZipOutputStream(fs))
{
await foreach (var blob in blobs)
{
var blobName = blob.Name;
var blobClient = sourceContainerClient.GetBlobClient(blobName);
//var downloadResponse = await blobClient.DownloadAsync();
//var streamContent = downloadResponse.Value.Content;
var entry = new ZipEntry(blobName);
zipOutputStream.PutNextEntry(entry);
await blobClient.DownloadToAsync(zipOutputStream);
}
}
}
BlobClient targetBlob = targetContainerClient.GetBlobClient(outputBlobName);
using (FileStream fs = new FileStream(outputBlobName, FileMode.Open))
{
await targetBlob.UploadAsync(fs);
}
}
}
}

Can upload image using ASP.NET WEB API but not when deployed

I am using ASP.NET WEB API to upload image to server. But when i upload the source code of my web api to gearhost.com and make a post request. I am unable to post the image. This is my web api controller code:
[Route("upload")]
[HttpPost]
public async Task<string> Upload()
{
var ctx = HttpContext.Current;
var root = ctx.Server.MapPath("/uploads/");
var provider = new MultipartFormDataStreamProvider(root);
try
{
await Request.Content
.ReadAsMultipartAsync(provider);
foreach (var file in provider.FileData)
{
var name = file.Headers
.ContentDisposition
.FileName;
// remove double quotes from string.
name = name.Trim('"');
var localFileName = file.LocalFileName;
var filePath = Path.Combine(root, "files", name);
// File.Move(localFileName, filePath);
// SaveFilePathSQLServerADO(localFileName, filePath);
// SaveFileBinarySQLServerADO(localFileName, name);
// SaveFilePathSQLServerEF(localFileName, filePath);
SaveFileBinarySQLServerEF(localFileName, name, filePath);
if (File.Exists(localFileName))
File.Delete(localFileName);
}
}
catch
{
return "Error";
}
return "File uploaded successfully!";
}
public void SaveFileBinarySQLServerEF(string localFile, string fileName, string filePath)
{
// 1) Get file binary
byte[] fileBytes;
using (var fs = new FileStream(localFile, FileMode.Open, FileAccess.Read))
{
fileBytes = new byte[fs.Length];
fs.Read(fileBytes, 0, Convert.ToInt32(fs.Length));
}
// 2) Create a Files object
var file = new tblimage()
{
Data = fileBytes,
Names = fileName,
ContentType = filePath
};
// 3) Add and save it in database
using (var ctx = new coachEntities())
{
ctx.tblimages.Add(file);
ctx.SaveChanges();
}
}
Here is the successful call from localhost:
Image posted through localhost
However when deployed the same code and make request through postman then I get this error:
Image posted through live server
Maybe, "uploads" doesn't have write permission
Check the permission in your uploads folder.
Go to properties-- security
Give the read write permission.
Though it is not good idea to return the exception details in live code. As you are not maintaining log. For testing, Please return the exception details. Also, how are you getting the response like "unable to upload, try again" because it is not there in your code

Change Content Type for files in Azure Storage Blob

I use only Microsoft Azure Storage and no other Azure products/services. I upload files to my storage blob via ftp type client (GoodSync), and I need to change the content type of all the files based on their file extension after they are already in the Blob. I have looked around and have not found out how to do this without having one of their VPS with PowerShell. What are my options and how do I accomplish this? I really need step by step here.
I recently had the same issue so I created a simple utility class in order to "fix" content type based on file's extension. You can read details here
What you need to do is parse each file in your Azure Storage Containers and update ContentType based on a dictionary that defines which MIME type is appropriate for each file extension.
// Connect to your storage account
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(storageConnectionString);
// Load Container with the specified name
private CloudBlobContainer GetCloudBlobContainer(string name)
{
CloudBlobClient cloudBlobClient = _storageAccount.CreateCloudBlobClient();
return cloudBlobClient.GetContainerReference(name.ToLowerInvariant());
}
// Parse all files in your container and apply proper ContentType
private void ResetContainer(CloudBlobContainer container)
{
if (!container.Exists()) return;
Trace.WriteLine($"Ready to parse {container.Name} container");
Trace.WriteLine("------------------------------------------------");
var blobs = container.ListBlobs().ToList();
var total = blobs.Count;
var counter = 1;
foreach (var blob in blobs)
{
if (blob is CloudBlobDirectory) continue;
var cloudBlob = (CloudBlob)blob;
var extension = Path.GetExtension(cloudBlob.Uri.AbsoluteUri);
string contentType;
_contentTypes.TryGetValue(extension, out contentType);
if (string.IsNullOrEmpty(contentType)) continue;
Trace.Write($"{counter++} of {total} : {cloudBlob.Name}");
if (cloudBlob.Properties.ContentType == contentType)
{
Trace.WriteLine($" ({cloudBlob.Properties.ContentType}) (skipped)");
continue;
}
cloudBlob.Properties.ContentType = contentType;
cloudBlob.SetProperties();
Trace.WriteLine($" ({cloudBlob.Properties.ContentType}) (reset)");
}
}
_contentTypes is a dictionary that contains the appropriate MIME type for each file extension:
private readonly Dictionary _contentTypes = new Dictionary()
{
{".jpeg", "image/jpeg"},
{".jpg", "image/jpeg" }
};
Full list of content types and source code can be found here.
Here you are a refreshed version for latest Azure.Storage.Blobs SDK. I'm using .Net 5 and console app.
using Azure.Storage.Blobs.Models;
using System;
using System.Collections.Generic;
using System.IO;
var contentTypes = new Dictionary<string, string>()
{
{".woff", "font/woff"},
{".woff2", "font/woff2" }
};
var cloudBlobClient = new BlobServiceClient("connectionstring");
var cloudBlobContainerClient = cloudBlobClient.GetBlobContainerClient("fonts");
await cloudBlobContainerClient.CreateIfNotExistsAsync();
var blobs = cloudBlobContainerClient.GetBlobsAsync();
await foreach (var blob in blobs)
{
var extension = Path.GetExtension(blob.Name);
contentTypes.TryGetValue(extension, out var contentType);
if (string.IsNullOrEmpty(contentType)) continue;
if (blob.Properties.ContentType == contentType)
{
continue;
}
try
{
// Get the existing properties
var blobClient = cloudBlobContainerClient.GetBlobClient(blob.Name);
var properties = await blobClient.GetPropertiesAsync();
var headers = new BlobHttpHeaders
{
ContentType = contentType,
CacheControl = properties.CacheControl,
ContentDisposition = properties.ContentDisposition,
ContentEncoding = properties.ContentEncoding,
ContentHash = properties.ContentHash,
ContentLanguage = properties.ContentLanguage
};
// Set the blob's properties.
await blobClient.SetHttpHeadersAsync(headers);
}
catch (RequestFailedException e)
{
Console.WriteLine($"HTTP error code {e.Status}: {e.ErrorCode}");
Console.WriteLine(e.Message);
Console.ReadLine();
}
}

Resources