I'm using Microsoft.WindowsAzure.Storage.* library from C#.
This is how I'm uploading things to storage:
// Store in storage
CloudStorageAccount storageAccount = CloudStorageAccount.Parse("...connection string...");
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference("pictures");
// Create container if it doesnt exist
container.CreateIfNotExists();
// Make available to everyone
container.SetPermissions(new BlobContainerPermissions
{
PublicAccess = BlobContainerPublicAccessType.Blob
});
// Save image
CloudBlockBlob blockBlob = container.GetBlockBlobReference("blah.jpg");
blockBlob.UploadFromByteArray(byteArrayThumbnail, 0, byteArrayThumbnail.Length);
blockBlob.Properties.ContentType = "image/jpg"; // *** NOT WORKING ***
All the things I upload to the storage are being saved with content type "application/octet-stream", even though I'm using the setter with value "image/jpg" (see the last line in my code).
So question #1: Why isn't working the ContentType setter?
And question #2: If I manually change the content type to "image/jpg", using Windows Azure management portal, and then copy the absolute URI of the file to the browser's address field, and press enter, the jpg file is downloaded instead of displayed. Isn't this mime type supposed to be displayed instead of downloaded? How do I change this?
Actually you don't have to call SetProperties method. In order to set content type while uploading the blob, just set the ContentType property before calling the upload method. So your code should be:
// Save image
CloudBlockBlob blockBlob = container.GetBlockBlobReference("blah.jpg");
blockBlob.Properties.ContentType = "image/jpg";
blockBlob.UploadFromByteArray(byteArrayThumbnail, 0, byteArrayThumbnail.Length);
and that should do the trick.
After you make any changes to Properties, you have to make a call to CloudBlockBlob.SetProperties() to actually save those changes.
Think of it as something similar to LINQ-to-Entities. You can make any changes you want to your local object, but until you call SaveChanges(), nothing is actually saved.
Using the new SDK Azure.Storage.Blobs
BlobHttpHeaders blobHttpHeaders = new BlobHttpHeaders();
blobHttpHeaders.ContentType = "image/jpg";
blobClient.SetHttpHeaders(blobHttpHeaders);
Unfortunately, none of the answers provided here is currently working for the latest SDK (12.x.+)
With the latest SDK, the content type should be set via BlobHttpHeaders.
var _blobServiceClient = new BlobServiceClient("YOURCONNECTIONSTRING");
var containerClient = _blobServiceClient.GetBlobContainerClient("YOURCONTAINERNAME");
var blob = containerClient.GetBlobClient("YOURFILE.png");
var blobHttpHeader = new BlobHttpHeaders();
blobHttpHeader.ContentType = "image/png";
var uploadedBlob = await blob.UploadAsync(YOURSTREAM, blobHttpHeader);
Obviously best to set it on create like Gaurav Mantri's answer, if you are past that point and need to update the other answers here may mess you up.
// GET blob
CloudBlobContainer container = blobClient.GetContainerReference(containerName);
CloudBlockBlob blockBlob = container.GetBlockBlobReference(fileName);
// if you don't do this you'll wipe properties you didn't mean to
await blockBlob.FetchAttributesAsync();
// SET
blockBlob.Properties.ContentType = mimetype;
// SAVE
await blockBlob.SetPropertiesAsync();
with the new version of the Azure Blob SDK this is no longer working.
this worked for me:
CloudBlockBlob blockBlob = cloudBlobContainer.GetBlockBlobReference(blobName);
blockBlob.Properties.ContentType = contentType;
await blockBlob.SetPropertiesAsync();
I am trying to download a block blob from Azure using C#. The code I am using is below.
In other tests, I am able to list the blobs in the container but I am unable to download a specific blob. It doesn't give me an exception or error but the file when created locally is empty.
I have cleared out the connection string for obvious reasons.
Does my code look ok?
var containerName = "samples-workitems";
CloudStorageAccount storageAccount = CloudStorageAccount.Parse("DefaultEndpointsProtocol=xxxxxxxxxxxxxxxxxx.windows.net");
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference(containerName);
try {
CloudBlockBlob blockBlob = container.GetBlockBlobReference("file.png");
var localPath = string.Format("C:\\users\\user\\downloads\\file.png");
blockBlob.DownloadToFileAsync(localPath, FileMode.Create);
catch
{
}
You know what - I have been stuck on this since yesterday and since posting this 2 minutes ago - I removed...
CloudBlockBlob blockBlob = container.GetBlockBlobReference("file.png");
var localPath = string.Format("C:\\users\\user\\downloads\\file.png");
these 2 lines of code from within the try statement and put them above it. It now works?!
Why is that?
I have an Azure blob storage setup with a couple of files in it. I am able to download the files into a Stream when they are small (KB sized), but when the files are a little larger (MB sized) I get a 404 error. I have manually downloaded from the portal one of the images that is returning 404 fine and have resized that image and then uploaded the smaller image back to the container and I can then grammatically download it into a stream.
Here is the code that I'm using to download the blob
private static byte[] PerformDownload(string fileName, CloudBlobContainer container)
{
var blockBlob = container.GetBlockBlobReference(fileName);
using (var memoryStream = new MemoryStream())
{
blockBlob.DownloadToStream(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
var binaryReader = new BinaryReader(memoryStream);
var bytes = binaryReader.ReadBytes((int)memoryStream.Length);
return bytes;
}
}
The container is passed into this method and as I mentioned I can download some files from the container without issue, but if you need that code I can add that as well
The container is retrieve using the standard examples that you find, but here is the code
private static CloudBlobContainer GetContainer(string containerName)
{
var storageAccount = CloudStorageAccount.Parse(ConnectionString);
var container = CreateContainerIfNeeded(storageAccount, containerName);
return container;
}
private static CloudBlobContainer CreateContainerIfNeeded(CloudStorageAccount storageAccount, string containerName)
{
var blobClient = storageAccount.CreateCloudBlobClient();
var container = blobClient.GetContainerReference(containerName);
container.CreateIfNotExists();
return container;
}
Also Case sensitivity is not the issue because the container's name is 2017-106 and the file is 4448.jpg.
I am able to download the files into a Stream when they are small (KB sized), but when the files are a little larger (MB sized) I get a 404 error.
Currently max size of a block blob is approx. 4.75 TB, storing MB-sized data in a block blob, which should not cause Azure Blob service return 404 when you access the blob. 404 error indicates that the specified blob does not exist, as Gaurav Mantri said, Blob name is case-sensitive, please make sure the filename (blob name) you provided indeed exists in your container.
Besides, If only that specific blob can not be found, but it is really existing in your container, you can create support request to report it.
I have an API written in C# that is meant to recieve a file from frontend. As of now it's a byte array and i want to convert this to a .mp4 file and then send it to my azure media service with the blobstorage. I do not want to store it locally and i can't read it from disk either. What is the best approach for this?
I create my CloudBlobClient like so:
private CloudBlobClient CloudBlobClient()
{
var storageAccount = CloudStorageAccount.Parse(ConfigurationManager.ConnectionStrings["StorageConnection"].ConnectionString);
var blobStorage = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobStorage.GetContainerReference(Constants.VideoBlobContainer);
if (container.CreateIfNotExist())
{
var permissions = container.GetPermissions();
permissions.PublicAccess = BlobContainerPublicAccessType.Container;
container.SetPermissions(permissions);
}
return blobStorage;
}
Then I have this method that i've started
private Uri UploadToStorage(CloudBlobClient blobStorage, byte[] video, VideoSize size)
{
var uniqueBlobName = GetVideoUriAsString(VideoId, Type, size);
CloudBlockBlob blob = blobStorage.GetBlockBlobReference(uniqueBlobName);
I'm not sure how to proceede here. I have been looking around a lot on the web for approaches but all I find is example of console applications reading from disk.
Is there anyone familliar with this type of uploading to media serivces?
You're on your way there, although you should just obtain the reference to the blob from the blob container from the first method. Very rough but here you go:
public void uploadBytesToBlobWithMimeAndStorageCreds(string theFolder, string theFileName, byte[] videoBytes, string theMimeType)
{
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(ConfigurationManager.ConnectionStrings["StorageConnection"].ConnectionString);
CloudBlobClient client = storageAccount.CreateCloudBlobClient;
CloudBlobContainer container = client.GetContainerReference(theFolder);
CloudBlob blob = container.GetBlobReference(theFileName);
blob.UploadByteArray(theBytes);
blob.Properties.CacheControl = "max-age=3600, must-revalidate";
blob.Properties.ContentType = theMimeType; // e.g. "video/mp4"
blob.SetProperties();
}
Is is possible to rename an Azure Storage Blob using the Azure Storage API from a Web Role? The only solution I have at the moment is to copy the blob to a new blob with the correct name and delete the old one.
UPDATE:
I updated the code after #IsaacAbrahams comments and #Viggity's answer, this version should prevent you from having to load everything into a MemoryStream, and waits until the copy is completed before deleting the source blob.
For anyone getting late to the party but stumbling on this post using Azure Storage API V2, here's an
extension method to do it quick and dirty (+ async version):
public static class BlobContainerExtensions
{
public static void Rename(this CloudBlobContainer container, string oldName, string newName)
{
//Warning: this Wait() is bad practice and can cause deadlock issues when used from ASP.NET applications
RenameAsync(container, oldName, newName).Wait();
}
public static async Task RenameAsync(this CloudBlobContainer container, string oldName, string newName)
{
var source = await container.GetBlobReferenceFromServerAsync(oldName);
var target = container.GetBlockBlobReference(newName);
await target.StartCopyFromBlobAsync(source.Uri);
while (target.CopyState.Status == CopyStatus.Pending)
await Task.Delay(100);
if (target.CopyState.Status != CopyStatus.Success)
throw new Exception("Rename failed: " + target.CopyState.Status);
await source.DeleteAsync();
}
}
Update for Azure Storage 7.0
public static async Task RenameAsync(this CloudBlobContainer container, string oldName, string newName)
{
CloudBlockBlob source =(CloudBlockBlob)await container.GetBlobReferenceFromServerAsync(oldName);
CloudBlockBlob target = container.GetBlockBlobReference(newName);
await target.StartCopyAsync(source);
while (target.CopyState.Status == CopyStatus.Pending)
await Task.Delay(100);
if (target.CopyState.Status != CopyStatus.Success)
throw new Exception("Rename failed: " + target.CopyState.Status);
await source.DeleteAsync();
}
Disclaimer: This is a quick and dirty method to make the rename execute in a synchronous way. It fits my purposes, however as other users noted, copying can take a long time (up to days), so the best way is NOT to perform this in 1 method like this answer but instead:
Start the copy process
Poll the status of the copy operation
Delete the original blob when the copy is completed.
There is practical way to do so, although Azure Blob Service API does not directly support ability to rename or move blobs.
You can, however, copy and then delete.
I originally used code from #Zidad, and in low load circumstances it usually worked (I'm almost always renaming small files, ~10kb).
DO NOT StartCopyFromBlob then Delete!!!!!!!!!!!!!!
In a high load scenario, I LOST ~20% of the files I was renaming (thousands of files). As mentioned in the comments on his answer, StartCopyFromBlob just starts the copy. There is no way for you to wait for the copy to finish.
The only way for you to guarantee the copy finishes is to download it and re-upload. Here is my updated code:
public void Rename(string containerName, string oldFilename, string newFilename)
{
var oldBlob = GetBlobReference(containerName, oldFilename);
var newBlob = GetBlobReference(containerName, newFilename);
using (var stream = new MemoryStream())
{
oldBlob.DownloadToStream(stream);
stream.Seek(0, SeekOrigin.Begin);
newBlob.UploadFromStream(stream);
//copy metadata here if you need it too
oldBlob.Delete();
}
}
While this is an old post, perhaps this excellent blog post will show others how to very quickly rename blobs that have been uploaded.
Here are the highlights:
//set the azure container
string blobContainer = "myContainer";
//azure connection string
string dataCenterSettingKey = string.Format("DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}", "xxxx",
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
//setup the container object
CloudStorageAccount cloudStorageAccount = CloudStorageAccount.Parse(dataCenterSettingKey);
CloudBlobClient blobClient = cloudStorageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference(blobContainer);
// Set permissions on the container.
BlobContainerPermissions permissions = new BlobContainerPermissions();
permissions.PublicAccess = BlobContainerPublicAccessType.Blob;
container.SetPermissions(permissions);
//grab the blob
CloudBlob existBlob = container.GetBlobReference("myBlobName");
CloudBlob newBlob = container.GetBlobReference("myNewBlobName");
//create a new blob
newBlob.CopyFromBlob(existBlob);
//delete the old
existBlob.Delete();
Copy the blob, then delete it.
Tested for files of 1G size, and it works OK.
For more information, see the sample on MSDN.
StorageCredentials cred = new StorageCredentials("[Your?storage?account?name]", "[Your?storage?account?key]");
CloudBlobContainer container = new CloudBlobContainer(new Uri("http://[Your?storage?account?name].blob.core.windows.net/[Your container name] /"), cred);
string fileName = "OldFileName";
string newFileName = "NewFileName";
await container.CreateIfNotExistsAsync();
CloudBlockBlob blobCopy = container.GetBlockBlobReference(newFileName);
if (!await blobCopy.ExistsAsync())
{
CloudBlockBlob blob = container.GetBlockBlobReference(fileName);
if (await blob.ExistsAsync())
{
// copy
await blobCopy.StartCopyAsync(blob);
// then delete
await blob.DeleteIfExistsAsync();
}
}
Renaming is not possible. Here is a workaround using Azure SDK for .NET v12:
BlobClient sourceBlob = container.GetBlobClient(sourceBlobName);
BlobClient destBlob = container.GetBlobClient(destBlobName);
CopyFromUriOperation ops = await destBlob.StartCopyFromUriAsync(sourceBlob.Uri);
long copiedContentLength = 0;
while (ops.HasCompleted == false)
{
copiedContentLength = await ops.WaitForCompletionAsync();
await Task.Delay(100);
}
await sourceBlob.DeleteAsync();
You can now with the new release in public preview of ADLS Gen 2 ( Azure Data Lake Storage Gen 2)
The Hierarchical Namespace capability allows you to perform atomic manipulation of directories and files which includes Rename operation.
However, make note of the following:
"With the preview release, if you enable the hierarchical namespace, there is no interoperability of data or operations between Blob and Data Lake Storage Gen2 REST APIs. This functionality will be added during preview."
You will need to make sure you create the blobs (files ) using ADLS Gen 2 to rename them. Otherwise, wait for the interoperability between Blob APIs and ADLS Gen 2 to be added during the preview time period.
Using Monza Cloud's Azure Explorer, I can rename an 18 Gigabyte blob in under a second. Microsoft's Azure Storage Explorer takes 29 sec to clone that same blob, so Monza is not
doing a copy. I know it is fast because immediately after the Monza rename, clicking the container in Microsoft Azure Storage Explorer shows the blob with the new name.
The only way at the mement is to move the src blob to a new destination/name. Here is my code to do this
public async Task<CloudBlockBlob> RenameAsync(CloudBlockBlob srcBlob, CloudBlobContainer destContainer,string name)
{
CloudBlockBlob destBlob;
if (srcBlob == null && srcBlob.Exists())
{
throw new Exception("Source blob cannot be null and should exist.");
}
if (!destContainer.Exists())
{
throw new Exception("Destination container does not exist.");
}
//Copy source blob to destination container
destBlob = destContainer.GetBlockBlobReference(name);
await destBlob.StartCopyAsync(srcBlob);
//remove source blob after copy is done.
srcBlob.Delete();
return destBlob;
}
Here is a code sample if you want the blob lookup as part of the method:
public CloudBlockBlob RenameBlob(string oldName, string newName, CloudBlobContainer container)
{
if (!container.Exists())
{
throw new Exception("Destination container does not exist.");
}
//Get blob reference
CloudBlockBlob sourceBlob = container.GetBlockBlobReference(oldName);
if (sourceBlob == null && sourceBlob.Exists())
{
throw new Exception("Source blob cannot be null and should exist.");
}
// Get blob reference to which the new blob must be copied
CloudBlockBlob destBlob = container.GetBlockBlobReference(newName);
destBlob.StartCopyAsync(sourceBlob);
//Delete source blob
sourceBlob.Delete();
return destBlob;
}
There is also a way without copying your blob to rename it, and without running any script: mounting Azure Blob storage to your OS: https://learn.microsoft.com/bs-latn-ba/azure/storage/blobs/storage-how-to-mount-container-linux
Then you can just use mv and your blob will be renamed instantly.
Using Azure Storage Explorer is the easiest way to manually rename a blob. You can download it here https://azure.microsoft.com/en-us/features/storage-explorer/#overview
If you set the ContentDisposition property with
attachment;filename="yourfile.txt"
The name of the download over http will be whatever you want.
I think Storage was built with the assumption that data would be stored in a way with unique identifiers primarily used as the filenames. Issuing Shared Access Signatures for all downloads is a bit weird though, so this isn't ideal for some people.
But I think abstracting away the user-facing filename is overall a good practice and encourages a more stable architecture overall.
This worked for me in live environment of 100K Users having file sizes no more than 100 mb. This is similar synchronous approach to #viggity's answer. But the difference is that its copying everything on Azure side so you don't have to hold Memorystream on your server for Copy/Upload to new Blob.
var account = new CloudStorageAccount(new Microsoft.WindowsAzure.Storage.Auth.StorageCredentials(StorageAccountName, StorageAccountKey), true);
CloudBlobClient blobStorage = account.CreateCloudBlobClient();
CloudBlobContainer container = blobStorage.GetContainerReference("myBlobContainer");
string fileName = "OldFileName";
string newFileName = "NewFileName";
CloudBlockBlob oldBlob = container.GetBlockBlobReference(fileName);
CloudBlockBlob newBlob = container.GetBlockBlobReference(newFileName);
using (var stream = new MemoryStream())
{
newBlob.StartCopyFromBlob(oldBlob);
do { } while (!newBlob.Exists());
oldBlob.Delete();
}