Check if a file exists in azure blob container - azure

I have an azure blob container name "x" and I want to check if a file name "a.jpg" exists in that blob or not and return true or false based on that. It seems easy but there isn't a clear answer when I google it.

It is clearly mentioned in the document, if you are using c# you can use ExistsAsync method
public async Task<bool> FileExists(string fileName)
{
return await directory.GetBlockBlobReference(fileName).ExistsAsync();
}

You can also use REST Api if you want.
https://learn.microsoft.com/en-us/rest/api/storageservices/get-blob-metadata
A lot more trouble... but works too.
Request The Get Blob Metadata request may be constructed as follows.
HTTPS is recommended. Replace myaccount with the name of your storage
account:
TABLE 1 GET or HEAD Method Request URI HTTP Version
https://myaccount.blob.core.windows.net/mycontainer/myblob?comp=metadata
https://myaccount.blob.core.windows.net/mycontainer/myblob?comp=metadata&snapshot=

Related

Get Azure Storage to Return blob URL when listing storage container contents

I'm trying to use a service principal to access and list the contents of an Azure storage container using PowerShell. I've been able to get a token for the service principal and use that to access the storage account but when I run this command
$iwrParams = #{
'Uri' = 'https://myaccount.blob.core.windows.net/mycontainer?restype=container&comp=list'
'UseBasicParsing' = $true
'ContentType' = 'application/xml'
'Headers' = #{
'Authorization' = "Bearer $($token.access_token)"
'x-ms-version' = '2017-11-09'
}
}
Invoke-WebRequest #iwrParams
I get a return from this but the return xml content never shows the URL for each returned blob. If you look at the return at https://learn.microsoft.com/en-us/rest/api/storageservices/enumerating-blob-resources#list-blobs-and-snapshots it shows that for each blob it should return a Name, Url and various other properties. When I run the code above I get everything but the Url.
Now the really interesting thing is if I change the container access to anonymous public and run this code, I get the Url returned as expected.
$iwrParams = #{
'Uri' = 'https://myaccount.blob.core.windows.net/mycontainer?comp=list'
'UseBasicParsing' = $true
'ContentType' = 'application/xml'
}
Invoke-WebRequest #iwrParams
The issue honestly seems to be having to include restype=container when accessing with any sort of authentication.
My question is does anyone know a way to get the URL for each blob returned when not using anonymous access to list container contents?
I'd like to do this without resorting to the Az modules.
The reason you're not able to see the URL returned in the response is because of the storage REST API version used by your code (2017-11-09). Essentially the Blob URL property was removed from the response starting with REST API version 2013-08-15. From this link:
In version 2013-08-15 and newer, the EnumerationResults element
contains a ServiceEndpoint attribute specifying the blob endpoint, and
a ContainerName field specifying the name of the container. In
previous versions these two attributes were combined together in the
ContainerName field. Also in version 2013-08-15 and newer, the Url
element under Blob has been removed.
Regarding your comment about why you can see the URL property if you list blobs anonymously, this is happening because if no REST API version is specified in the request, Storage Service uses the oldest REST API version to process the request if default version has not been set. From this link:
If an anonymous request to a general-purpose storage account does not
specify the x-ms-version header, and the default version for the
service has not been set using Set Blob Service Properties, then the
service uses the earliest possible version to process the request.
Considering you would want to use Azure AD based authorization, the earliest version you will be able to use is 2017-11-09 thus it will not be possible to get the Blob URL returned in the response body.
One option would be to manually construct the Blob URL by using Blob Container URL and Blob name. Other option would be to use Shared Key Authorization instead of Azure AD authorization and specifying a version earlier than 2013-08-15 for x-ms-version request header in your requests. You will need to manually compute Authorization header value in this case using instructions provided here.

Images uploaded to Azure blob storage unavailable when browsing by direct URL

I have uploaded a number of images to a Blob container on an Azure storage account of type StorageV2 (general purpose v2).
These were uploaded programmatically. Here's the code I used:
public Task CopyFile(string fileName, string targetPath)
{
var blobRef = Container.GetBlockBlobReference(targetPath);
blobRef.Properties.ContentType = GetContentType(fileName);
return blobRef.UploadFromFileAsync(fileName);
}
public string GetContentType(string fileName)
{
var provider = new FileExtensionContentTypeProvider();
if (!provider.TryGetContentType(fileName, out var contentType))
{
contentType = "application/octet-stream";
}
return contentType;
}
Container is an initialized CloudBlobContainer instance.
When I use the Storage Explorer I can see the uploaded files. If I view the properties of any file it lists a Uri property. However, if I copy the value (a URL) and paste into a browser I see the following error page:
<Error>
<Code>ResourceNotFound</Code>
<Message>
The specified resource does not exist. RequestId:12485818-601e-0017-6f69-56c3df000000 Time:2019-08-19T08:35:13.2123849Z
</Message>
</Error>
But if I double-click the file in Storage Explorer it downloads the image correctly. The URL it uses is the same as the one I copied earlier as far as I could tell, except for some additional querystrings that look like this: ?sv=2018-03-28&ss=bqtf&srt=sco&sp=rwdlacup&se=2019-08-19T16:49:38Z&sig=%2FJs7VnGKsjplalKXCcl0XosgUkPWJccg0qdvCSZlDSs%3D&_=1566204636804
I assume this must mean my blobs are not publically available, but I can't find any setting that will make my images available publically at their known URI. Can anyone point me in the right direction here? Thank you.
Check the access level that set to your container.
If that is a Private then you will have the error that you experiencing: ResourceNotFound
As far as I know, if you container's access level is Private, you use the direct url to access the blob then you will get the error. If you want to access it, you need to generate a SAS token for it.
For more details, please refer to
https://learn.microsoft.com/en-us/azure/storage/blobs/storage-manage-access-to-resources
https://learn.microsoft.com/en-us/azure/storage/common/storage-sas-overview

Can't implement azure web app service access to azure storage container (blob) using MSI

I have an azure resource group which contains Web App Service and Storage with BLOB container. My web app (.NET Core) tries to retrieve and show an image from container. The container has no public access to content (access level is private). I created system assigned identity for my app and gave it Reader role in storage access control (IAM).
This is how I get access to blobs in app's code:
const string blobName = "https://storagename.blob.core.windows.net/img/Coast.jpg";
string storageAccessToken = await GetStorageAccessTokenAsync();
var tokenCredential = new TokenCredential(storageAccessToken);
var storageCredentials = new StorageCredentials(tokenCredential);
var blob = new CloudBlockBlob(new Uri(blobName), storageCredentials);
ImageBlob = blob.Uri;
GetStorageAccessTokenAsync() does this:
var tokenProvider = new AzureServiceTokenProvider();
return await tokenProvider.GetAccessTokenAsync("https://storage.azure.com/");
Then the image is displayed by
<img src="#Model.ImageBlob" />
I don't get any exceptions in my code, but image from the BLOB container isn't shown with 404 error (specified resource doesn't exist) in browser console.
When I change container's access level to "blob" (public access), app works fine and the image is displayed.
Apparently, it is something wrong with getting credentials part, but I couldn't find any working example nor detailed explanations how it actually should work.
Any help is very appreciated.
UDPATE:
Thank you all who responded. So, it seems I've got two problems here.
1) I don't get credentials properly.
I can see that "AzureServiceTokenProvider" object (Microsoft.Azure.Services.AppAuthentication) that I create, has empty property PrincipalUsed at the runtime.
My application deployed to Azure App Service, which has system managed identity and that identity (service principal) is given permissions in Azure storage (I changed permission from account Reader to Storage Blob Data Reader as was suggested).
Shouldn't it get all data needed from the current context? If not, what I can do here?
2) I use wrong method to show image, but since the app has no access to storage anyway I can't fix it yet.
But still - what is the common way to do that in my case? I mean there is no public access to storage and I use "CloudBlockBlob" to reach images.
Reader gives access to read the control plane, but not the data plane. The role you need is Storage Blob Data Reader, which gives access to read blob contents.
For more details about this, check out: https://learn.microsoft.com/en-us/azure/role-based-access-control/role-definitions#data-operations-example
When you use <img src="#Model.ImageBlob" />, no authorization header is sent in the request by the browser. In your code, you are fetching the token, but the token is not being sent in the authorization header when the image is being fetched. So, storage API thinks this is an anonymous request. This is the reason you are getting a 404.
You need to send auth code when fetching the image. This code works for me
public async Task<ActionResult> Image()
{
const string blobName = "https://storage.blob.core.windows.net/images/image.png";
string storageAccessToken = await GetStorageAccessTokenAsync().ConfigureAwait(false);
var tokenCredential = new TokenCredential(storageAccessToken);
var storageCredentials = new StorageCredentials(tokenCredential);
var blob = new CloudBlockBlob(new Uri(blobName), storageCredentials);
Stream blobStream = blob.OpenRead();
return File(blobStream, blob.Properties.ContentType, "image.png");
}
In the view, I use
<img src="/Home/Image" />
Finally, I got it to work. First of all, the part of code regarding getting token and image from Azure storage was OK. The second problem with displaying image in RazorPages application I resolved, using this code in view:
<form asp-page-handler="GetImage" method="get">
<img src="/MyPageName?handler=GetImage" />
</form>
and corresponding code in model:
public async Task<ActionResult> OnGetGetImageAsync()
{
//getting image code and returning FileContentResult
}
But I'm still thinking: whether is more simple way to do that? Something like to add image collection to the model, fill it using "OnGet..." handler and then display its content using in view. I didn't find a way to use model properties in <img> tag. Does anyone have some suggestions?

Azure blob, controlling filename on download

Our application enables users to upload files via a web browser. The file name gets mapped to a GUID and the blob name becomes the GUID. When the user clicks the file in our application it should download to their file system (show save as, etc) using the original file name and not the GUID blob name.
I found this post, and similar posts that describe how to set the Content-Disposition on the blob when downloading through a Shared Access Signature.
Friendly filename when public download Azure blob
However, our situation is a little different. We set a single SAS at the Container level (technically this is called a Shared Access Policy I believe -- you can have up to 5 at any given time). When downloading the blob, we simply append the SAS to the end of the uri, and use...
window.location.href = blobUri + containerSAS;
...to download the blob. This downloads the blob, but uses the GUID filename.
How can we take an existing SAS that applies to the Container and have the blob download as the original filename?
Keep in mind this is a slightly different use case from a SAS applied to an individual blob in that...
The SAS is at the Container level (it seems this is the best practice vs. individual SAS's for each blob).
We are downloading from javascript (vs. C# code where you can set the headers).
I have tried to set the Content-Disposition of the blob during the upload process (PUT operation), but it doesn't seem to make a difference when I download the blob. Below, you can see the Content-Disposition header being set for the PUT request (from Fiddler).
Thanks!
This post pointed us in the right direction to change the file name with the the ContentDisposition property Azure.Storage.Blobs
BlobContainerClient container = OpenContianer(containerName);
BlobClient blob = container.GetBlobClient(sourceFilename);
var Builder = new BlobSasBuilder(BlobSasPermissions.Read, DateTimeOffset.Now.AddMinutes(10));
Builder.ContentDisposition= $"attachment; filename = {destFileName} ";
var SasUri = blob.GenerateSasUri(Builder);
I have a solution. I think it's more of a workaround, but for each file to be downloaded, I make a server call and create a special SAS for the download operation. I can set Content-Disposition with that, and now the GUID named blobs are downloading with their original filenames.

Upload file to Azure Blob Storage directly from browser?

Is it possible to create an html form to allow web users to upload files directly to azure blob store without using another server as a intermediary? S3 and GAW blobstore both allow this but I cant find any support for azure blob storage.
EDIT November 2019
You can now refer to the official documentation:
Azure Storage JavaScript Client Library Sample for Blob Operations
Azure Storage client library for JavaScript
Initial answer
There is a New Azure Storage JavaScript client library for browsers (Preview).
(Everything from this post comes from the original article above)
The JavaScript Client Library for Azure Storage enables many web development scenarios using storage services like Blob, Table, Queue, and File, and is compatible with modern browsers
The new JavaScript Client Library for Browsers supports all the storage features available in the latest REST API version 2016-05-31 since it is built with Browserify using the Azure Storage Client Library for Node.js
We highly recommend use of SAS tokens to authenticate with Azure Storage since the JavaScript Client Library will expose the authentication token to the user in the browser. A SAS token with limited scope and time is highly recommended. In an ideal web application it is expected that the backend application will authenticate users when they log on, and will then provide a SAS token to the client for authorizing access to the Storage account. This removes the need to authenticate using an account key. Check out the Azure Function sample in our Github repository that generates a SAS token upon an HTTP POST request.
Code sample:
Insert the following script tags in your HTML code. Make sure the JavaScript files located in the same folder.
<script src="azure-storage.common.js"></script/>
<script src="azure-storage.blob.js"></script/>
Let’s now add a few items to the page to initiate the transfer. Add the following tags inside the BODY tag. Notice that the button calls uploadBlobFromText method when clicked. We will define this method in the next step.
<input type="text" id="text" name="text" value="Hello World!" />
<button id="upload-button" onclick="uploadBlobFromText()">Upload</button>
So far, we have included the client library and added the HTML code to show the user a text input and a button to initiate the transfer. When the user clicks on the upload button, uploadBlobFromText will be called. Let’s define that now:
<script>
function uploadBlobFromText() {
// your account and SAS information
var sasKey ="....";
var blobUri = "http://<accountname>.blob.core.windows.net";
var blobService = AzureStorage.createBlobServiceWithSas(blobUri, sasKey).withFilter(new AzureStorage.ExponentialRetryPolicyFilter());
var text = document.getElementById('text');
var btn = document.getElementById("upload-button");
blobService.createBlockBlobFromText('mycontainer', 'myblob', text.value, function(error, result, response){
if (error) {
alert('Upload filed, open browser console for more detailed info.');
console.log(error);
} else {
alert('Upload successfully!');
}
});
}
</script>
Do take a look at these blog posts for uploading files directly from browser to blob storage:
http://coderead.wordpress.com/2012/11/21/uploading-files-directly-to-blob-storage-from-the-browser/
http://gauravmantri.com/2013/02/16/uploading-large-files-in-windows-azure-blob-storage-using-shared-access-signature-html-and-javascript
The 2nd post (written by me) makes use of HTML 5 File API and thus would not work in all browsers.
The basic idea is to create a Shared Access Signature (SAS) for a blob container. The SAS should have Write permission. Since Windows Azure Blob Storage does not support CORS yet (which is supported by both Amazon S3 and Google), you would need to host the HTML page in the blob storage where you want your users to upload the file. Then you can use jQuery's Ajax functionality.
Now that Windows Azure storage services support CORS, you can do this. You can see the announcement here: Windows Azure Storage Release - Introducing CORS, JSON, Minute Metrics, and More.
I have a simple example that illustrates this scenario here: http://www.contentmaster.com/azure/windows-azure-storage-cors/
The example shows how to upload and download directly from a private blob using jQuery.ajax. This example still requires a server component to generate the shared access signature: this avoids the need to expose the storage account key in the client code.
You can use HTML5 File API, AJAX and MVC 3 to build a robust file upload control to upload huge files securely and reliably to Windows Azure blob storage with a provision of monitoring operation progress and operation cancellation. The solution works as below:
Client-side JavaScript that accepts and processes a file uploaded by user.
Server-side code that processes file chunks sent by JavaScript.
Client-side UI that invokes JavaScript.
Get the sample code here: Reliable Uploads to Windows Azure Blob Storage via an HTML5 Control
I have written a blog post with an example on how to do this, the code is at GitHub
It is based on Gaurav Mantris post and works by hosting the JavaScript on the Blob Storage itself.
Configure a proper CORS rule on your storage account.
Generate a Shared Access Signature from your target container.
Install the blob storage SDK: npm install #azure/storage-blob.
Assuming your file is Blob/Buffer/BufferArray, you can do something like this in your code:
import { ContainerClient } from "#azure/storage-blob";
const account = "your storage account name";
const container = "your container name";
const sas = "your shared access signature";
const containerClient = new ContainerClient(
`https://${account}.blob.core.windows.net/${container}${sas}`
);
async function upload(fileName, file) {
const blockBlobClient = containerClient.getBlockBlobClient(fileName);
const result = await blockBlobClient.uploadData(file);
console.log("uploaded", result);
}

Resources